Tumblring

I’ve decided to set up shop over on Tumblr. Check it out over at http://blog.bruzilla.com.

Ubuntu 9.10 on a Eee 1000HA: Amazing

Issues with Ubuntu mainline support for Netbooks, the Asus Eee models in particular, have spawned various Ubuntu variants such as Eeebuntu and Easy Peasy. Up until recently I was using Eeebuntu 3.01 on my Eee 1000HA, but a hard drive problem fouled that up so I reconsidered my OS options.

With the recent release of Ubuntu 9.10 (Karmic Koala) I decided to try the mainline Netbook Remix distribution. It’s virtues have now won me over:

  • Sound works perfectly out of the box (previously had to manually update ALSA), and no issues with speakers still playing with headphones plugged in
  • Resume from hibernate is WAY faster than w/ previous Ubuntus
  • Ubuntu Netbook Remix launcher interface is much snappier

The only thing that didn’t work out of the box, but didn’t work with Eeebuntu either, is wifi. It’s super flaky using the default ath5k, and despite various attempted fixes, I always have to fall back on using ndiswrapper.

Netbook + cloud for developing in Ruby, on the go

What’s important in a laptop?
So what are the most important features of a portable laptop for doing Ruby development? It may vary a bit from person to person, but here’s what they are for me:

  • Speed – If you keep autospec/autotest running in the background to continuously like I do, you want your tests to run fast and not bog down your laptop while you’re writing code
  • Battery life – For a laptop that’s being used on the go in places where you may not always have access to power, this is a big deal
  • Size – It’s not really portable if it’s big and heavy
  • Price – I’m not willing to spend a lot of money on something that’ll be obsolete in a couple years

Pick two or three
Want a fast laptop that’s also cheap? No problem, but odds are it’s not going to be small/light or have good battery life (for example a Dell Inspiron 14). So that gets us 2 out of 4.

How about fast, good battery life and small/light? Also doable, but don’t expect it to be cheap. That’s 3 out of 4.

Yet another option is to get amazing battery life, tiny size and an extremely low price with a netbook. But then speed goes right out the window with their Atom processors, right? Maybe not…

Using the ‘net’ in netbook to make an ideal Ruby development laptop
One of the selling points for netbooks is that they don’t need to be exceptionally fast since they’re intended to be used primarily with web applications, which offload most of the computational heavy lifting onto web servers. I wondered if the same sort of offloading to the web could be done for my Ruby development. As an experiment I decided to give pushing my Ruby development “into the cloud” a try.

So far I’m impressed with how snappy this netbook is, thanks to pushing all running of ruby code onto a server. I’ll follow up with more details on how I’m going about this, since it’s not entirely trivial. This setup may not be for everyone (I wouldn’t ever try to pry your macbook pro away from you), but for those who are into netbooks it’s a great way to make fast Ruby development possible on these fun little laptops.

Software as a Service with Amazon FPS using Remit for Ruby

For those who actually want people to pay to use their web applications, and don’t want the headache of accepting credit card payments themselves, the payment process can be outsourced with such services as Paypal Website Payments Standard and Amazon Flexible Payments Service (FPS). Amazon’s FPS looked very developer-oriented, and I like the customer experience they provide, so I went that route. Thankfully someone had already written a Ruby API for Amazon FPS, called Remit. It’s very well crafted, and I was able to glean everything I needed to know from the source code, but it still would have been nice to have a few examples to work from as I started using it to do the software-as-a-service dirtywork I needed to get done. So for those who follow in this path, here are a few tidbits. Note that a lot of the work in setting up a payment system is properly handling all the ways that payments can fail, which I’m not going into here.

Most Remit operations require an instance of Remit::API, so here’s how to set one up. The first two arguments for Remit::API.new are the access key and secret key that you can obtain when logged in to your Amazon Web Services account, and the third argument indicates whether or not to use sandbox mode:


require 'remit'

api = Remit::API.new(
  "my access key",
  "my secret key",
  true
)

Here’s the one-time process of creating and installing your recipient and caller tokens, which are needed to receive payments. The arguments for the request here are:

  • payment_instruction – see the Amazon FPS docs for customizing this
  • caller_reference – a reference ID that you may or may not want to store
  • token_friendly_name – optional friendly name for the token
  • token_type – see the Amazon FPS docs for customizing this

request = Remit::InstallPaymentInstruction::Request.new(
  :payment_instruction => "MyRole == 'Recipient' orSay 'Role does not match';",
  :caller_reference => Time.now.to_i.to_s,
  :token_friendly_name => "Friendly Name For Your Token",
  :token_type => "Unrestricted"
)

install_recipient_response = api.install_payment_instruction(request)
install_recipient_response.token_id  # hold on to this

request = Remit::InstallPaymentInstruction::Request.new(
  :payment_instruction => "MyRole == 'Caller' orSay 'Role does not match';",
  :caller_reference => Time.now.to_i.to_s,
  :token_friendly_name => "TriggerFood Caller Token",
  :token_type => "Unrestricted"
)

install_caller_response = api.install_payment_instruction(request)
install_caller_response.token_id  # hold on to this

Now you can start directing users to Amazon to pay you. If you’re not really familiar with Amazon FPS yet, what happens is that you redirect your users to the URL this following code provides, where they have to log in or register for an Amazon account, and then specify their payment method. No actual payment is collected though, you just get a payment token back when the pipeline successfully redirects the user back to your site. Here are the arguments for getting the pipeline URL:

  • caller_reference – a reference ID that you may or may not want to store
  • recipient_token – the token you just produced above
  • transaction_amount – per-month price
  • recurring_period – length of recurring time period
  • return_URL – the URL you want users to be sent back to after they’ve gone through the Amazon “pipeline”
  • caller_key – same as access the key when creating the Remit::API instance

recurring_use_pipeline = api.get_recurring_use_pipeline(
  :caller_reference => Time.now.to_i.to_s,
  :recipient_token => install_recipient_response.token_id,
  :transaction_amount => price,
  :recurring_period => "1 Month",
  :return_URL => return_url,
  :caller_key => "my access key"
)

recurring_use_pipeline.url      # this is the URL you want to send your users to

When users are sent back to your site at the :return_URL you provide, you’ll need to process that URL to get the information about how what happened in the pipeline. You’ll need to provide both the URL from Amazon when they redirected the user to your site, as well as your secret yet (the same as used above in the API step).


pipeline_response = Remit::PipelineResponse.new(
  url,
  "my secret key"
)

There are 3 essential pieces of information you’ll be able to glean from the pipeline response:

  • pipeline_response.valid? – if this isn’t true, then the URL isn’t signed properly, so you don’t want to use it
  • pipeline_response.success? – make sure this is also true before trying to request payment
  • pipeline_response.tokenID – the token for the sender–you’ll use this when requesting payment.

Now to make the user actually pay. For a web app with monthly billing, you’ll probably request payment immediately after running the pipeline or after some trial period, and then again once every month. Setting a temporary decline policy of implicit retry here will make Amazon do the work of retrying the transaction until it is either successful or permanently failed. This requires handling instant payment notification requests, which we’ll get to next. Here are the arguments you’ll need to provide and an example:

  • caller_token_id – install_caller_response.token_id from above
  • recipient_token_id – install_recipient_response.token_id from above
  • sender_token_id – the pipeline_response.tokenID from the last step
  • transaction_amount – a Remit::RequestTypes::Amount object, which takes an :amount and :currency_code (see example)
  • charge_fee_to – should be “Recipient”, assuming you want Amazon’s fee to come out of the charge to the user
  • caller_reference – a reference ID that you may or may not want to store
  • temporary_decline_policy – needs to specify implicit retry if you want Amazon to do the dirty work (see example)

request = Remit::Pay::Request.new(
  :caller_token_id => "my caller token",
  :recipient_token_id => "my recipient token",
  :sender_token_id => pipeline_response.tokenID,
  :transaction_amount => Remit::RequestTypes::Amount.new(
    :amount => price,
    :currency_code => "USD"
  ),
  :charge_fee_to => "Recipient",
  :caller_reference => Time.now.to_i.to_s,
  :temporary_decline_policy => Remit::RequestTypes::TemporaryDeclinePolicy.new(
    :temporary_decline_policy_type => Remit::TemporaryDeclinePolicyType::IMPLICIT_RETRY
  )
)

pay_response = api.pay(request)

The pay response returns the following essential information:

  • pay_response.successful? – false indicates that the request failed, true indicates that the request was successful, but not necessarily the transaction
  • pay_response.transaction_response.success? – true if the payment transaction was a success
  • pay_response.transaction_response.initiated? – true if payment has been initiated, but needs to be handled asynchronously
  • pay_response.transaction_response.reinitiated? – true if transaction was temporarily declined, but has been initiated again
  • pay_response.transaction_response.temporary_decline? – true if the transaction is temporarily declined
  • pay_response.transaction_response.failure? – true if it’s a complete failure

For transactions that are of ‘initiated’, ‘reinitiated’ or ‘temporary_decline’ status, Amazon will keep trying the transactions until they complete, assuming the temporary decline policy has been set to implicit retry. I handle these by using the Amazon FPS Instant Payment Notification (IPN) service, which can be enabled and set to hit a URL of your choice in your Amazon Payments settings. In order to process these notifications, you just need to provide Remit with the request parameters and your secret key:


ipn_request = Remit::IpnRequest.new(
  params,
  "my secret key"
)

The IPN request has the following useful properties:

  • ipn_request.valid? – true if the IPN request was properly signed and had all the necessary info
  • ipn_request.status – if this is “SUCCESS”, then the payment went through, otherwise it has permanently failed.

So that’s the bare bones of what’s needed to use Amazon FPS to handle recurring payments. There’s a lot missing here, such as (assuming a Ruby on Rails app) the actions that will send the user to your Amazon FPS pipeline, receive the responses from the Amazon FPS pipeline and IPN service, and the code to handle recurring payments. Writing lots of tests for these models and controllers is highly recommended, as well as quite a bit of subsequent integration testing in sandbox mode. Enjoy!

Easy server migrations with Slicehost

I’ve been using Slicehost for over a year now, albeit just hosting the marketing page for TriggerFood and doing developmental testing, but have found them to be an exceptionally good hosting provider. Their management interface is easy to use and powerful, uptime has been perfect as far as I know, and various online help–especially their articles–have been incredibly useful. Now I’ve found something that’s further solidified my respect for them.

For my original slice, I set it up with Ubuntu 6.06 (the newest Long Term Support version at the time), which was tied to Ruby 1.8.4, and then set up nginx and mongrel for serving up my rails app. This was a reasonably good setup, but since then new and exciting things like Ubuntu 8.04 LTS and mod_rails (Phusion Passenger) have rolled out. After giving these some time to make sure any initial kinks were worked out, I started to think about upgrading. At the time I figured I’d have to reinstall my slice with the new Ubuntu, set up all the other software, and just do it all really quickly so my site wouldn’t be down that long. This hardly seemed ideal, and so eventually it dawned on me that I could just purchase a new slice, set it up, and then switch over and get rid of the old slice.

As compared with the first slice I got which took a couple of months to get a hold of, my new slice took just a couple of minutes to build. I was able to take my time and get all my Ruby, Rails, Apache and mod_rails stuff set up properly, and was pleased that things really did work well out of the box. Then I just changed the DNS records for my domain so they pointed to the new slice, and had a seamless transition to my new server. I kept my olde server around for a bit in case I needed to roll back, but after having the new server up and running for a while, I blew away the old one. Slicehost even prorated the monthly charge for having the 2nd slice, so if you do your server transition really quickly, you hardly pay anything.

Foxify Fixtures plugin: refactor those old fixtures to make them foxy

I’ve been enjoying using foxy fixtures on new Rails projects, and wanted to start using them on some older fixture-heavy projects, but couldn’t bring myself to manually change over from id-based to foxy name-based fixtures. It would involve finding all the foreign keys in each fixture, then looking up the names of the fixtures with those ID in other fixture files, and substituting in those names. It sounded tedious and easily automated, so I went in search of a plugin or code snippet to do the work. Finding none, I wrote a plugin, ‘Foxify Fixtures.’ Install it with:

script/plugin install http://foxify-fixtures-plugin.googlecode.com/svn/trunk

Make sure your app is safely checked in to version control, or make some sort of backup in case things go awry, and run:

rake db:fixtures:foxify

The plugin was developed around two apps that I wanted to refactor to use foxy fixtures, so in all likelihood it won’t work on other apps without some tweaking. Feel free to contribute back any fixes through the plugin’s new home in Google Code: http://code.google.com/p/foxify-fixtures-plugin.

New Blog: Thought Triggers

In order to separate blog posts on software development and startup stuff from posts on food, health and TriggerFood, I’ve started a separate blog, Thought Triggers. So from now on, expect this blog (Edgy Developments) to have posts that would be of interest to people in the web application world, and the new blog to be of more interest to people who could benefit from TriggerFood.

Saddening statistics on Irritable Bowel Syndrome in youth

A recent article reports that 6 percent of middle schoolers and 14 percent of high schoolers suffer from IBS(irritable bowel syndrome)–such shocking and unfortunate figures. The only good news that the article is reporting on a recent study showing that low dose antidepressants improve IBS symptoms for these youth, something that’s already been found for adults. This just serves as another reminder that medical knowledge is constantly expanding, and it’s important to make use of medical advances that doctors can offer, in addition to health management techniques like using a food an health journal.

TriggerFood has a new look!

While developing TriggerFood, I’ve been doing a decent job with software development and system administration, but have had a harder time getting up to speed with web design. I’ve heard that when developing web applications, one must recognize one’s strengths and weaknesses, and seek professionals for those rough spots. Plus, I only have so much time, so it helps to offload some of the work. So a few weeks ago I signed up on elance.com, and used the service to procure the services of a talented web designer. It was a great experience, and I highly recommend it to anyone seeking to outsource portions of their projects.

I think the key to having a good experience with hiring a freelancer is coming up with a very well specified task, so that it’s clear on both ends what’s supposed to be done. Clear task descriptions are also likely to attract more (and perhaps better) bids, since a vague description is a good indicator that the person outsourcing the job doesn’t know what they want.

As a first time user of Elance, I also really appreciated the escrow, milestones and private message board features. These all go a long way toward making a first time user feel safe using the system.

See the snazzy result at www.triggerfood.com.

Health concerns with potential trigger foods abound

It’s amazing how often the potential usefulness of TriggerFood comes to mind in everyday conversation. Here are just a few recent health concerns that came up as potentially having trigger foods:

Next Page »