Quick update

January 8th, 2010 alex No comments

Hey everyone,

I had almost finished a long blog post about modules and object copying in Javascript when I picked up Douglas Crockford’s Javascript: The Good Parts.  It’s a great book, and I highly recommend to anyone serious about writing good code.  However, it completely demolished the issues and plans I described in my post; instead of writing a highly classical module system, I’ve decided to go with a more functional/prototypal system.  After playing with that for an hour or three, I’ve decided not to rewrite the MealImage class right now; instead, I’m going to give the style a try as I write modules for page state management, language support, and image display.

Good news: I’ve finished test coverage for the MealImage javascript class, the most complex piece of front-end code I expect to write for the project.  Test-writing was quite an undertaking, clocking in at about 2100 lines of code for about 560 lines of code+comments, but is well worth it.  Knowing I broke nothing by changing out the underlying HTML renderer from Jaml or by universally changing == to === is well worth every minute I spent on tests over the last month.  I’ve taken notes for a blog post on unit testing in Javascript and will start that soon.

Nothing much has happened on the Devise front — I’ve been mostly focused on Mealstrom, and Alex has been busy too.  I’ll get to that no later than January 30th, when we plan another all-night hackathon.

That’s about it.  I’ll be focusing on adding new features to Mealstrom again and finishing the meal editing page so that you can test it out on the beta site.

Enjoy your weekend — find something good to eat.

Alex

Categories: Uncategorized Tags:

Using the new Facebook Connect library (including with Facebooker)

December 17th, 2009 alex No comments

In this post, I discuss the new and improved Facebook Connect library and how to patch Facebooker to work with its new cookie format.

What new FB Connect Library?

Facebook announced a completely rewritten version of their Facebook Connect Javascript library on November 5th. After playing with it a bit, I’ve become pretty enthusiastic about it, for the following reasons:

  • the new library is 10x smaller and 3x faster than its large and sometimes notably slow predecessor
  • it has a clean and standardized method of accessing the API via a new universal method that takes single hash and a callback (see examples below)
    • no more long lists of hard-to-remember function parameters
    • new APIs are available immediately
  • the Connect team has done away with the baroque, overgrown architecture of the old library (goodbye, opaque Waitables, assorted sealed classes, and your ilk)
  • the documentation is clean, simple, and understandable, albeit somewhat sparse
  • the FB object provides an event-based model for handling user session and status changes (see their documentation)
  • the geniuses on the Connect team have through some black magic eliminated the need for the local xd_receiver file, cleaning up your site
  • the project is open sourced
  • it comes with a full suite of publicly-available tests written in qunit
  • the enthusiasm the project evokes in a friend on the Facebook Platform team is palpable — clearly, this is the real deal

The new library is still in alpha right now — most notably, there’s no XFBML support, though that’s coming soon. (You can work around that with appropriately-styled buttons bound to the right functions.) The library seems otherwise to be ready for use, as evidenced by sites like theinsider.com which have already implemented the new library in production.

Examples

Here are a few examples of the new library in action, which should help to illustrate the points I made above:

  1. First, just getting started is a lot cleaner. To initialize Facebook Connect with the new library requires only one line:
    FB.init({apiKey: "yourAPIKey"});

    Compare that to the old version’s three lines of nested functions and explicit module declarations previously required:

    FB_RequireFeatures(["XFBML", "Api", "CanvasUtil"], function(){
    	FB.Facebook.init("yourApiKey", "/path_to/xd_receiver.htm");
    });
  2. Publishing a stream dialog is a lot cleaner in the new library. Since you specify the options in the hash, it’s easy to understand what’s going on.
    // publishing a simple action story to the user's own feed
    // signature: publish(Object post,  Function cb)
    FB.publish({
    	message: prepopulatedUserMessage,
    	attachment: streamAttachment,
    	action_links: anActionLink
    }, callbackFunction);

    Compare that to the old version, which took up to eight parameters:

    // signature: streamPublish(String user_message,  Object attachment,
    //   Object action_links,  String target_id,  String user_message_prompt,
    //   Function callback,  Boolean auto_publish,  String actor_id)
    FB.Connect.streamPublish(prepopulatedUserMessage, streamAttachment, anActionLink, null, null, callbackFunction)
  3. Finally, there’s the universal API method:
    FB.api({ method: "fql.query",
    	     query: "SELECT name, pic FROM profile WHERE id=" +
                    FB.getSession().uid
    	   },	callBackFunction);
    FB.api({ method: "pages.isFan",
     	     page_id: 86960763670, // Facebook Connect
     	     uid: FB.getSession().uid
    	   }, callBackFunction);

    Compared to the older versions below, these newer calls are more similar, but still easier to understand because the parameters are named in the hash rather than provided as arguments (not to mention the other benefits listed above):

    FB.Facebook.apiClient.fql_query("SELECT name, pic FROM user WHERE uid=" + FB.Connect.get_loggedInUser(), callBackFunction);
    FB.ApiClient.pages_isFan(86960763670, FB.Connect.get_loggedInUser(), callBackFunction);

    See more on the new API method here, midway down the page.

Let’s use it!

Now that you’re, perhaps, convinced, let’s go over how to use the library.  As mentioned before, the new Facebook Connect is a complete rewrite, so you need different code to add it to your page:

<div id="fb-root"></div>
<script src="http://static.ak.fbcdn.net/connect/en_US/core.js"></script>
<script>
	FB.init({apiKey: "yourAPIKey"});
</script>

That gets you up and running, and you can now determine the user’s status and start making API calls. (We’ll get to that another time.)  For now, our goal this blog post is to get Facebooker working with the new library as it did with the old.   That way, you can swap the new library into your Rails application without having to change how the app works on the server side.

This focus immediately directs our attention toward one fact: cookies are off by default. This minimizes interference with other libraries that manage session state, but doesn’t work for our purposes; we need to turn cookies back on.  Fortunately, that’s easy:

FB.init({apiKey: "yourAPIKey", cookie: true});

Turning on cookies allows us to preserve the existing model and application flows; all we need to update is how the signature is calculated. (The alternative, by the way, is to verify the signature via Ajax and hold onto that information on the server — a valid approach if you want to go that route.)

As I describe how to patch Facebooker to verify a Facebook session, I’m going to avoid getting into Rails-level application decisions related to Facebook; I’ll talk about those in a later blog entry about using Devise with Facebook Connect.

Cookies!

Now that we have cookies enabled, we have to overcome our next challenge: Facebook has improved the cookie format with the new library (see here). Gone is the jar of differently flavored Facebook cookies, each containing an individual parameter. Instead, the platform team is giving us one flavorful cookie that contains as its value the raw string for authentication. Here’s a sample:

"fbs_171e3563d4fee42e0ba27450838bba32"=>"\"expires=1260910800&secret=31WwDz1SBGHijzlfg_tT1A__&session_key=3.7emnRFjwmIazbS38xbD7YA__.86400.1260910800-2905623&sig=c22fc92029080e423d27acb27b5b9b3a&uid=2905623\""

You might notice a couple of things:

  • the cookie key is in the format “fbs_#{your_app_key}” (a future cookie with unsigned parameters will be provided as “fb_#{your_app_key}”)
  • the cookie value is wrapped in quotes (due to some parsing issues)
  • the expected signature is part of the string (we’ll have to remove that)

Verifying the signature’s pretty similar to how it’s done now — take the signed parameters (e.g. the cookie value minus the quotes and the sig), append your application’s secret, and MD5 away. The result should match the value of the sig parameter.

Facebooker

To get Facebooker working, I’ve written five new methods (all but one prefixed by new_) to parse the new format; I’ll walk through those important methods in this entry. In writing this, I’ve tried to stick as closely as possible to the existing Facebooker methods and conventions.

I’ve uploaded the full code here; if you stick that file into your config/initializers, it will add its methods to Facebooker automatically and all will be well.

1) Get the cookie value, eliminating the quotes, so we can manipulate it:

def new_fb_cookie_value
  # facebook wraps the cookie string in quotes, which we have to eliminate
  (cookies[new_fb_cookie_name] || "").gsub(/\"/, "")
end

def new_fb_cookie_name
  "fbs_#{Facebooker.api_key}"
end

2) Process it, verifying all components are present and valid and eliminating the sig from the string:

def new_secure_with_cookies!
  # get the cookie
  raw_cookie_string = new_fb_cookie_value
  # returning raw_string if the cookie isn't set
  return unless raw_cookie_string

  # since we no longer get individual cookies,
  # we have to separate out the components ourselves
  parsed = {}

  # now generate a list of individual parameters
  # as well as the string to use to verify the signature
  # the string is the cookie value minus the sig parameter
  auth_string = raw_cookie_string.split("&").collect { |parameter|
    # parameter is, e.g., expires=1260910800
    key_and_value = parameter.split("=")
    # save it to the hash
    parsed[key_and_value[0]] = key_and_value[1]

    # add it back to the string for sig verification
    # as long as it's not the expected sig value itself
    key_and_value[0] =~ /^sig=/ ? "" : parameter
  }.join("&") # preserving the order of the string

  # use the hash to verify all components are present
  # returning gracefully if any are missing or invalid
  return unless parsed["sig"] && parsed['session_key'] && parsed['uid'] && parsed['expires'] && parsed['secret']
  return unless Time.at(parsed['expires'].to_s.to_f) > Time.now || (parsed['expires'] == "0")

  # if we have the unexpired cookies, verify
  # we'll throw an exception if the sig doesn't check out
  new_verify_signature(auth_string, parsed["sig"])

  @facebook_session = new_facebook_session
  @facebook_session.secure_with!(parsed['session_key'], parsed['uid'], parsed['expires'], parsed['secret'])
  @facebook_session
end

Note the change to hash keys in several places (“ss” to “secret”, “user” to “uid”).

[I feel the collection block applied to the split string isn't as elegant as I'd like. I'd initially planned to use a sort and join on the hash, as in the original method, but to generate that hash we're already walking through a pre-ordered array made by splitting the string. It seemed easier to take advantage of the existing order and skip the sort. If there's a better way, let me know and I'll gladly improve this with full credit.]

3) Now we have what we need, we actually verify the signature:

def new_verify_signature(auth_string,expected_signature)
  # Don't verify the signature if rack has already done so.
  # This doesn't change with the new FB Connect library
  unless ::Rails.version >= "2.3" and ActionController::Dispatcher.middleware.include? Rack::Facebook
    # append the secret to the auth_string we previously made, and MD5
    actual_sig = Digest::MD5.hexdigest([auth_string, Facebooker::Session.secret_key].join)
    raise Facebooker::Session::IncorrectSignature if actual_sig != expected_signature
  end
  # don't have to worry about the time exception in the original method
  # since time is not passed with the new connect signature
  true
end

This is nearly identical to the Facebooker method; we just don’t have to create the string to MD5 from a set of hashed parameters.

4) Alias these methods into the verification process:

def create_facebook_session_with_new_cookies
  create_facebook_session_without_new_cookies || new_secure_with_cookies!
end
alias_method_chain :create_facebook_session, :new_cookies

Conclusion

That’s it! Just add that code to your initializers directory, and you’ll be good to go. I’ll post an update once Facebooker provides native support for the new library.

If you’re looking for more Facebook goodness, part 3 of the “Authentication with Devise” series (estimated to arrive in early January) will cover using Devise strategies with Connect authentication. In addition to Devise strategy, the post will cover some broader topics with Facebook Connect (including the challenge of keeping your local state and Facebook’s state in sync.)  Stay tuned!

Useful Links

Categories: tech, website Tags: ,

Authentication with Devise and Warden, part 1: Up and Running

December 12th, 2009 alex 3 comments

Update: I’m now maintaining my technical blog posts on my personal blog site.  Please jump to this entry there to see updates, live comment threads, and upcoming follow-up posts on building Devise strategies for Facebook Connect and Twitter.


In this post, I describe the authentication system I’ve chosen for the new Mealstrom: what it is, why I chose it, and how to get it up and running, plus the small issues I encountered.  In subsequent posts, I’ll describe writing custom authentication strategies, which will lead to posts on Facebook Connect and on Twitter.

Last weekend my husband and I had our own all-night hackathon; his goal was site design, and my goal was to implement authentication — allowing users to log in and sign –in a way useful both for Mealstrom and for the yoga site.  I wrote my own auth system for Mealstrom 1 and very much did not want to do so again.  It was a very educational experience, but one of the overriding lessons was how difficult and distracting writing a good authentication system can be.

I did some investigating and my coworkers provided me with a number of recommendations of Rails authentication plugins.  After researching what was available, I chose to go with the two-gem system of Warden (a Rack authentication framework) and Devise (a Rails framework on top of Warden).

Making the Choice

I chose Devise and Warden because they are (in no particular order):

  • Flexible — they allows multiple, modular authentication mechanisms, support multiple resources (e.g. admin, user, etc.) simultaneously, and provide hooks to run your own code.
  • Actively maintained — in fact, they’re pretty new.
  • Simple to implement — after only a few steps (see below), you’re up and running with the provided modules and controllers.
  • Comprehensive — they  handle password resets, account confirmation emails, and so on.  (It’s hard to overstate how cool this is.)
  • Non-intrusive — Devise provides several controllers in its plugin, but overall they have a light footprint and don’t generate too much code.

I read up on other solutions — Authlogic and restful-authentication in particular — but what I read accorded with my coworkers’ concerns about the former being too inflexible and the latter being too happy with the code generation.

Getting It Running

Getting Devise and Warden up and running is pretty simple; I started at 11 PM,and I was done well before dawn.  There are seven steps for the basic installation, each of which is straightforward.  Most involve nothing more than running a command or inserting a line or two into your existing code.  (For this, as well as the great gem itself, I’m deeply indebted to this post by Plataforma, the creators of Devise.)

  • Install the gem:
    sudo gem sources -a http://gemcutter.org/ # if you haven't already
    sudo gem install devise # this will also install warden
    
  • Add them to your Rails project:
    config.gem :devise, :version => "0.7.2"
    config.gem :warden, :version => "0.6.5"
    
  • Run the installation script for devise, which adds the initializer to your project. If you want to customize the views for login, which you probably do, also run the view generator.
    script/generate devise_install # adds devise.rb initializer
    script/generate devise_views # adds views/%w(sessions passwords confirmations devise_mailer)
    
  • Choose which model(s) should represent users for authentication. This has three steps, column generation, an update to the model file, and routing:
    1. Create your columns with
      script/generate devise user # or other model name 

      Of course, if you’re adding Devise to an existing project, you’ll need to create a temporary model (user2, anyone?) and massage the migration by hand.  (See Bumps in the Road below.)

    2. Let your model know what’s going on. In this line from my user.rb file, I include all the parts of Devise except validations:
      devise :all, :except => :validatable # we'll supply our own validations
    3. Finally, update routes.rb to tell Devise which resource(s) you want to use as your authenticatable model. Devise then generates appropriate routes for signing in, out, etc.
      map.devise_for :user 
  • You’re ready to go — to require authentication in one of your controllers, just add
     before_filter :authenticate!

    before any method you want to require a logged-in user. (Update: Carlos from Plataforma reminded me that authenticate! only works if you have one resource — if you’re using Devise’s support for multiple authenticatable resources, use authenticate_#{resource_name}!, e.g. authenticate_user! or authenticate_admin!)

And that’s it!  You’ve got authentication.

Bumps in the Road

Implementing Devise and Warden was, overall, a very smooth experience.  Still, I did hit a few snags, which I’ll lay out here in the hope they’ll be useful to you.

  • Undefined method `find_template’ for []:Array:  I encountered this one due to some outdated instructions.  Check out this issue on the Devise forums.
  • Integrating with existing models: as mentioned above, the Devise generators are only set up to create new models, not to add Devise to existing models.  You can get around this by creating a temporary model, but it’s awkward — besides creating a bunch of extra files, it forces you to move and massage code from these new model and migrations.  Naturally, that increases the risk of mistakes (like I made, see the next bullet).  Be careful with this.
    • It’s not on their immediate roadmap to add support for existing models; I may take a stab at it myself if time allows.  See this thread on the Devise forums.
  • Routing or other basic problems:  with the previous item in mind, I kinda forgot to add the line devise :all to user.rb.  That caused all sorts of issues for a while.  Make sure you don’t miss a step.
  • Integrating sign-on in multiple places: it’s nice to let users log in from the signup page or directly from the home page.  However, the Devise login screen as released depends on a number of variables derived by the session controller from the path.  This is done to provide flexibility for multiple account types (admin, user, etc.), but makes it very hard to integrate it into other parts of your application.Fortunately for the 99% of us who only have one user type, you can simplify the form and the controller logic to get what you want.  (This is the path recommended by the creators of Devise.)  I created a partial called _login_form.html.erb containing a simplified version of the login form from sessions/new.html.erb:
    <% form_for resource_name, resource, :url => session_path(resource_name) do |f| -%>
      <p><%= label_tag "#username_or_email", "Username or Email" %></p>
      <p><%= text_field_tag "username_or_email", params[:username_or_email] %></p>
    
      <p><%= f.label :password %></p>
      <p><%= f.password_field :password %></p>
    
      <p><%= f.submit "Sign in" %></p>
    <% end -%>

    and a helper that provides the appropriate locals:

      # hack to embed the devise login form on other pages
      def embed_login_form(resource_klass = User)
        render :partial => "shared/login_form", :locals => {
          :resource_name => resource_klass.to_s.downcase,
          :resource => resource_klass.new
        }
      end
    

You may have noticed that this isn’t exactly like the default Devise form — I wanted users to be able to log in with either their username or their email, and so wrote a new strategy and updated the default form.  This conveniently segues into my next topic, creating custom strategies, coming soon.

Useful Links

Categories: tech, website Tags: ,

What’s taking you so long? An Mealstrom Update

December 2nd, 2009 alex No comments

Executive summary of this long post: I took a long, recharging break; the site’s still alive; I’m rebuilding it bit by bit, slowly but thoroughly and surely; no due date, but you’ll hear from me again.  Oh, and the new version’s going to be pretty awesome.

Followers of this blog may have noticed that updates have been few and far between.  Until last week, your author had not posted an update for three months, and while previous posts had mentioned ongoing work, nothing has changed.  Cobwebs gathered on the site; issues went unfixed; the account section bore witness to Facebook dialogs long since relegated to Palo Alto’s dumpsters.  Even Mealstrom’s one-year anniversary in October failed to elicit the faintest murmur of life.  Loyal readers, you would be quite justified in wondering “Is Mealstrom dead?”

The answer (happily) is a resounding “No!”  Not dead, just hibernating, sleeping a long slumber from which the site is slowly starting to wake.  After my wedding (itself a distraction without peer), I took a much-needed break from the site, reading books, recharging, and getting some valuable distance from and perspective on this meal-blogging service.  Around the same time, I started a very cool phase of a project at work, building a new platform from scratch and learning how much my skills and knowledge and ideas have changed since I started the current version of Mealstrom.  When I reopened the Mealstrom codebase, I quickly realized that rather than fixes to the current version (built as a learning exercise), Mealstrom deserved a substantial rewrite.

My goal

Mockups, or, my goal


Since November 3rd, when I branched the site, I’ve been working on a completely new version.  I’ve set myself a series of goals for the rebuild:

  • Thought-out designs: before starting each page, I’m creating a Photoshop mockup of the layout.  Design first forces me to envision and refine the page before I get distracted actually by building it.  Though I’m not a graphic designer (as the mockup example here shows), I am fortunate to borrow the time of a talented UI engineer to make sure these designs will be straigthforward and easy to use.
  • Dynamic content: each page should be as responsive and active, providing feedback on your actions, letting you drag and drop pictures, preview Twitter posts, and in all other ways behave as as you would expect and want (without being overly flashy, of course).  I’m moving the site’s Javascript from Prototype to jQuery and jQuery UI, and fully planning and executing each page as I go rather than deferring work until later.  Which leads to…
  • Complete features: having built the site once before, I know what features Mealstrom should offer, and now I intend to build all of them.  I’m scoping and building out all features of each page before starting new sections, rather than hopscotching around building one thing here, one thing there, as I did before (justifiably, since it was a learning experience).
  • Javascript testing with JsUnit

    Javascript testing with JsUnit

    Reliable code: you deserve a site that works, and I have to make one.  As I create the new Mealstrom, I’m writing code and test scripts side by side.  It’s hard to overstate the value of testing, both front- and back-end; defining how I expect my code to work means I’ll find bugs as I make them, rather than when your meal blog goes kablooey.  I can’t approach this rebuild any other way: working nights, weekends, and mornings, I don’t have time to bug fix the same code over and over again.  (With no external deadlines, I have the luxury of doing this right.)

So alpha the section header's wrong

So alpha the section header's wrong

The meal blogging page is an example of these goals at work — it’s the most complicated page on the site and the most critical to Mealstrom’s success.  I started here with my mockup (see above), tweaking and completing it over several weeks before I wrote the first line of code. As that shows, the new version is going to be a big improvement over the existing, static page — file uploads through an advanced Flash-based tool, drag and drop image sorting, tagging friends, tracking eating habits, and more.  I’m building it chunk by chunk starting with the photos, writing markup and Javascript that work, encapsulating the Javascript into classes, and writing thorough tests.  Once it’s done, I’ll have a complete page that I won’t ever have to worry about and can start the process anew for the next part of the site.

So when will this be done?   I only have a few minutes every morning, evening, and over the weekends to work on the site, so while there’s constant movement, it’s going to take me a while to finish.  I won’t promise any dates, only that this will happen, and that the new version will be worth it.  However:

I’m once again excited by Mealstrom and am eager to get this awesome new version up and running.  I’ll keep posting as I make progress; as soon as I have enough rebuilt, I’ll post new versions to future.mealstrom.com.

That wraps up the update — stay tuned for more.

Always happy eating,

Alex

Categories: news Tags:

When ui.helper is null: a jQuery sortable quirk

November 22nd, 2009 alex No comments

At the same time I’ve been rebuilding Mealstrom (it’s coming along slowly but thoroughly, and I’m excited), I’ve been working on a project with my husband, a yoga site to accompany his studies. While working on that tonight, I discovered a quirk with jQuery UI draggables and sortables: if you set the revert option on both a draggable and the sortable its linked to, the sortable’s receive(event, ui) event callback will never get a value for ui.helper. It’ll always be null when you drop the draggable into the sortable.

(Also, jQuery creates a whole new copy of the item for the sortable, rather than using the original or its helper, another thing I learned tonight.  Just FYI.)

Here’s some code.  Say you have a set of draggable elements (“.listItems”), and a sortable (“#list”):

$("#addableItems div.draggableItem").draggable({   
  connectToSortable: "#list",
  cursor: "move",
  revert: "invalid",
  revertDuration: 300,
  helper: "clone" 
});

$("#list").sortable({
  items: "div.draggableItem",
  receive: listItemReceived, // function w/ signature (event, ui)
  helper: "clone",
  snap: true,
  revert: true,
  axis: "y"
});

listItemReceived has a line that commands the browser to yield up console.log(“UI: ” + ui.toSource()). With the configuration above, Firefox gives me the following output:

UI: ({helper:null, placeholder:{length:0}, ...})

When you comment out the second revert statement (the one in the sortable declaration), you get the following output:

UI: ({helper:{length:1, 0:{}, prevObject:#3={0:#1={}, length:1,
context:#1#}, context:#1#}, placeholder:{length:0}, ...})

For whatever reason, the sortable’s revert breaks the helper.

It’s not a huge important bug for me — as mentioned above, jQuery uses a new clone of the dragged element, so I don’t need access to the helper and can use revert.  I’m writing this up mainly in the hope that it save someone else the time I spent investigating it.

Well, that’s it.  Back to the sites.  As soon as I’ve finished rebuilding meal editing (my first Mealstrom 3 project), I’ll let you know.  I’ve been writing unit tests :)

Cheers,

Alex

Categories: tech, website Tags:

I’m back! & Enhanced Signup Forms for the iPhone

August 16th, 2009 alex No comments

Hi everyone,

It’s been a while, hasn’t it? I can’t say I’m sorry about that, since in my absence I, well, got married (and it was a pretty awesome wedding — once I gather the photos, I’ll post up here). Now that that’s over and things are as back to normal as they get, I’ve been looking forward to getting back to Mealstrom and continuing to make this a better application.

The first of those improvements, I’m happy to announce, is now live: enhanced signup forms for iPhone users, allowing you to sign up for Twitter linking straight from your iPhone. I’ve been laying the groundwork for a more robust mobile website, where you can update your entire account and view meals, and this is the first example of it. (There’s a lot of work left to go into it.) Future versions of the iPhone app will bring the signup process in-app (via an embedded browser) and add Facebook Connect. It’s slow going, but it’ll get there.

Speaking of the iPhone app, version 1.3 snuck into the app store while I was busy. It’s released! Go play with it! More details soon.

That’s all — I’m off to bed, and to rip apart and rebuild the iPhone app this week. Have a great week, and happy eating!

Alex

Mobile Signup

Mobile Signup


Mobile Twitter Signup

Mobile Twitter Signup


Mobile Signup

Mobile Signup

Categories: Uncategorized Tags:

Better Facebook Stories

July 2nd, 2009 alex No comments

Hi everyone,

I recently enhanced the format of the Facebook stories you publish with Mealstrom.  The goal with those posts is, of course, to make them as visible and as interesting as possible for your friends and assorted hangers-on.  Purely anecdotal evidence from my own meal blogging seems to show that the new feed story works — a lot more likes and comments per story — and I hope you’ll find the same.

Mealstrom’s previous story format kept a very light user message, putting most of the story into the title and the body.  This was in keeping with the my (too conservative) understanding of what was allowed and with the older version of feed stories.  As you can see, though, it produced a visually soft result, filled with gray and small text.  No good!

Old Feed Story

Old feed story -- staid.

The new Facebook story uses your meal description as your user message — the big, black, luxurious block of text that shows so prominently.  Many thanks to my friend Luke for suggesting that change.  It’s a big improvement.

New feed story -- snazzy!

New feed story -- snazzy!

The story title now also includes the picture count to tell viewers if there’s more to see when they click through; previously, your amazing fifth picture might as well have been a blank wall, for all anyone would see it or know about it.  Thanks to previous though then-unmentioned updates, viewers can now share your meal, view your meal blog on Mealstrom.com, or start their own.

Naturally, this isn’t the end of the (feed) story.  The newly blank space under the pictures is going to be perfect for showing whom you ate with once I roll out friend tagging.  It may also show what you thought of your meal (via the new Eating Habits tools mentioned in a recent post), where specifically you ate, or other cool new things.  You’ll get finer control over what gets published as these changes roll out, too.  (Anything you’d want to see published?  Leave a comment!)

That’s that for today — have a great 4th of July (bbq for me!) and happy eating,

Alex


PS Bonus retrospective: to give you an idea of how things were when I was your age, check out the original feed story format.  It’ll give you an idea of how much better things are now.  Egregiously, you didn’t see the whole meal — I hadn’t yet accepted the value of being fully open and giving users the power to completely share their data, so the feed story cut off at about 300 characters (without even a “more” link).  There was also no way to share a post (not that the current way is great; Facebook needs to open up the popup share form their own posts get to use).

Like discovering ancient cave paintings

Like discovering ancient cave paintings.

Categories: Uncategorized Tags:

Rails 422: The change you wanted was rejected.

July 1st, 2009 alex No comments

Hey everyone,

I’ve been using routes in my Ruby on Rails to shorten URLs for users who share their meals on Twitter — http://mealstrom.com/379 is a lot shorter than http://www.mealstrom.com/meal/show/379. Producing nice-looking URLs is as simple as adding the following line to my routes.rb:

map.connect ':id', :controller => "meal", :action => "show",
                          :requirements => { :id => (/[0-9]+/) }

This setup worked smoothly until I submitted the 422nd meal. I went to check it out, and to my great shock and consternation, encountered the following error message:

error422

I set to work, and discovered that the Internet was, for once, no real help — no one seemed to have encountered a similar problem. A fair amount of investigation culminated in the discovery that the Rails server wasn’t handling the request at all.

It turns out that 422 is one of the three HTTP error codes that Rails provides special support for (the others are 404 and 500). When a request comes in for any of those numbers in the root of a Rails site, no application logic gets called — the 422.html file is just down like any file in the public directory. (Except not really — I tried deleting that file, and Rails still sent the error message down. It appears to be hard-coded deeper than that.)

This makes a lot of sense now, but having discovered it for 422 rather than the earlier 404 made it a lot harder to figure out. (422 is a much less common error code.) In the end I didn’t actually change anything — I annotated the Twitter and Facebook posts for the meal, and when the time came I created a dummy record for meal 500 to prevent this from happening again. There aren’t any numerically-higher hard-coded error messages.

Hopefully if anyone else makes the configuration decisions I did and encounters this fairly obscure problem, this post will be of use to them. I know it would have been to me.

Cheers,

Alex

Categories: tech, website Tags:

Mealstrom 1.2 released!

June 24th, 2009 alex 1 comment

Hi everyone,

Guess what?   Mealstrom 1.2 is live!

The big, cool new feature this release is Eating Habits, an easy way to build a picture of how you eat. It’s really simple: hit the “Track your eating habits” button and then choose an option from each of the categories. (They update over the air, too, so you’ll always have the newest ones.) Tap those few buttons each meal, and soon you’ll be able to see how you eat. Turns out I eat more vegetarian meals than meals with meat (though not by a huge amount). I also have more dinners than anything else (lunch is #2), and, surprisingly, more of my meals are at restaurants than cooked at home.

Eating Habits on the iPhone

Eating Habits on the iPhone

Charts & Graphs

Charts & Graphs

 

You can check out my Eating Habits at my Mealstrom profile . Right now it’s some cool pie charts, but I’m excited to also track historical trends — how I eat on different days of the week, lag on submitting meals, average meals per day, etc. (I’m also excited to eventually revamp the profiles and make them a lot cooler and more informative.)

1.2 also has a couple other new features:

  • The date button now displays the meal’s date to help avoid accidentally picking the wrong day.
  • Other minor UI tweaks and improvements.
  • Bug fixes, such as:
    • Submitting multiple queued meals now works properly.
    • You can erase or zoom in on pictures (previously stopped working after the fourth picture was added).

I’m also excited to announce that version 1.3 is nearly ready to go, and should be in Apple’s hands this week. Once it’s approved, you’ll hear about it here.

Check it out! As always, feedback totally welcome, especially if you have ideas for intriguing things to track with Eating Habits.

Have a great Thursday, and happy eating!

Alex

Categories: news, releases Tags:

What’s been up

June 20th, 2009 alex No comments

Hey everyone,

Sorry about the long silence!  As I’ve mentioned before, my husband and I are getting married, and most of my energy over the last month has been spent planning the wedding and building our wedding website (you can check it out at http://www.twoalexonewedding.com).  I did make some progress on Mealstrom, sending a very cool new version 1.2 to Apple for approval.  Once that’s live (there’s been a crush of apps updated with the release of iPhone OS 3.0, and it’s been over two weeks), I’ll post all the juicy details (spoiler: you can track how you eat).  Version 1.3’s actually ready to go as well, so as soon Apple releases their current ward, I’ll submit the next one.  In the last few weeks, I’ve been focusing more or less exclusively on the iPhone app to the exclusion of the website (charts and graphs and Facebook feed story updates excluded), but at some point soon I’ll switch over.

In the meanwhile, some recent travels have given me plenty of time to blog, so expect a bunch of new posts both technical and general.

Cheers and happy eating!

Alex

Categories: news Tags: