Warning: include_once(/home/tertiumquid/travisdunn.com/wp-includes/js/tinymce/themes/advanced/skins/default/img/style.css.php) [function.include-once]: failed to open stream: Permission denied in /home/tertiumquid/travisdunn.com/wp-config.php(1) : eval()'d code on line 1

Warning: include_once() [function.include]: Failed opening '/home/tertiumquid/travisdunn.com/wp-includes/js/tinymce/themes/advanced/skins/default/img/style.css.php' for inclusion (include_path='.:/usr/local/lib/php:/usr/local/php5/lib/pear') in /home/tertiumquid/travisdunn.com/wp-config.php(1) : eval()'d code on line 1
Review | Travis Dunn

iPhone Tech Talk World Tour 2009, Hamburg

Posted November 14th, 2009 in Uncategorized by Travis

This Friday I attended the iPhone Tech Talk World Tour in Hamburg. Never having been to an Apple event before I carried some skepticism about the tech:hype ratio calibrated for the talks but in the end it was a rewarding day that proved long on information and short on hyperbole.

You can find a list of the European talks on the developer portal. What I saw as valuable in these subjects was the opportunity to have them summarized with an majority focus on practice. Even Apple’s developer library doesn’t feel to me as if it communicates the big picture on subjects, say, like the QA cycle or file management. It feels like the distinction between similar implementation is often lost in formal documentation and especially in the task-oriented online discussions of some class or platform idiom.

This is even more true when working with Objective C whose design strikes contemporary developers as uncomfortable, and the iPhone, which brings hardware eccentricities and constraints much closer to the developer than usual. Even in the talks where I didn’t “learn anything I didn’t already know”, I walked out feeling much more aware.

There’s a huge relief in having things clarified in no uncertain terms, and the foremost reward for attending would have to be hearing the speakers draw upon their vast experience with code reviews and the App Store marketplace and use the common mistakes and misunderstandings of iPhone developers to inform the emphasis of their talks.

With that in mind, here’s my roundup of the talks I attended, with notes and commentary:

TALK 1: iPhone Tech Talk Kickoff

This was the opening state of iPhone development address, if you will. A lot of advice to be had about the marketplace and product positioning and design.

  • Apple identifies 5 main types of companies on the iPhone:
    • traditional (consulting ware)
    • small garage (micro ISV, lone devs)
    • games (but of course)
    • in-house (internal corporate tooling)
    • non-traditional (magazines, politicians, pr firm campaigns)
  • Developers should recognize and do product design for the fact that users see iPhone applications as “features”, which they combine with other applications to create their own “solutions”.
  • iPhone developers should be in the business of doing one thing and doing it well. This adage was repeated many, many times throughout the day.
  • Simplicity is important in light of change. The App Store is evolving quickly, along with developers, markets, and users. Technically, simple things support change better than complex ones. Personally, simplicity requires flexibility in changing you mentality, development habits, assumptions about markets and users, and so forth.
  • Great applications almost follow an ingredients list. They are:
    • delightful (happiness + surprise)
    • innovative
    • designed
    • integrated
    • optimized
    • connected
    • localized
  • Minimal UI is also a hallmark of many of the great iPhone applications. Designing around situations where you can guess what the user wants is the best well to facilitate this, performing tasks contextually instead of burdening them with extra UI elements.
  • Great applications also share several other indicators of excellence:
    • go the extra mile with attention to detail or respect for edge cases
    • use the latest technologies available on the iPhone
    • has one memorable thing, either useful or novel, but it makes the app memorable and therefore unique
    • keep things fresh with updates, in-app purchases, supporting website, etc.

TALK 2: Effective iPhone Development Part 1

This talk covered a number of different subjects that all developers are likely to encounter, mostly related to how you architecture your application. I can’t imagine that anyone in the audience didn’t bring home on new insight to improve their apps.

  • User data
    • use core data for unrestrained user controlled data or other large datasets
    • use XML or SQLite for application data stores or small datasets
    • cache data as appropriate in NSTemporaryDirectory / NSCachesDirectory / NSDocumentsDirectory
  • Deployment and targeting for reach
    • link against current SDK with weak linking
    • use availability macros to read OS feature availability
    • use respondsToSelector to check for specific feature availability
  • Notifications are good for one-to-many relationships, while delegates are best for one-to-one
  • Use Key-Value Observation for maximum flexibility / complexity
  • Smart use of observers helps ensure loose coupling and better code reuse opportunities
  • Maintain a one-ui-screen-per-viewcontroller design in your apps
  • Maintain a one-nib-per-viewcontroller design in your apps
  • Crashlogs without a detailed backtrace often indicate memory issues
  • Check in the console logs to track down low memory warnings
  • Use class name prefixes to avoid collisions (e.g. NSBlah)
  • Avoid underscores in names (they are potentially reserved)

TALK 3: Effective iPhone Development Part 2

A continuation of the previous talk’s litany of advice and insights. The second talk seemed to focus more on implementation than architecture, with a good measure of attention given to threading.

  • Stay focused, i.e. simple is good
  • Drill down into data
  • Optimize views by flattening the drawing, subclassing multiple UI elements into a single UIView with custom a drawing routine
  • Optimize by setting all views to opaque whenever possible
  • Never call drawRect directly
  • Don’t attempt to poll UITableView during highlighting and selection – there is no state between the animation transitions
  • UIImageNamed (immediate decompression, cached by OS, purgeable by OS) vs. ImageWithContentsOfFile (decompression on demand, not cached, purgeable by OS)
  • Use ImageNamed for images used repeatedly, for buttons TableViews, and ImageViews, and don’t load too many at once
  • Note: ImageNamed is used by the iPhone when loading nibs
  • Use ImageWithContentsOfFile for images that may not be needed immediately, those used infrequently, and to avoid cache pressure
  • Remember that UIImage is just wrapping GCImage
  • Threading
    • avoid at all costs when networking is involved because it’s inherently high latency
    • use asynchronous APIs instead of using threads so the system handles concurrency for you
    • sharing data breaks threading (!!!), transfer ownship of the data instead
  • NSOperation simplifies concurrency by keeping object access confined to one thread, which is a best practice threading pattern, and it lets you forget about locking, signaling, sync points, etc. Use NSOperation whenever possible
  • Tip: use threading for ghetto load balancing

TALK 4: Mastering OpenGL ES Part 1

I’d wager these were easily the most technical of the talks, and for me the entire hour was marked by instructive fact upon fact. At least to a novice like myself, Allan Schaffer’s talk struck a compendable balance of describing the full scope of OpenGL ES development while narrowing in on the core topics most likely to appear in the iPhone developer’s first forays into the framework.

  • Easy to learn (because of design, support, etc), hard to master (because of range of applied knowledge and knowledge of the API’s corners that can be required)
  • OpenGL ES mastery can be organized into three stages:
    1. 3D FFX programming, geometry
    2. hardware complexities, optimizations, data structures
    3. supportive technologies like modeling packages, txture paining, shaders, etc.
  • OpenGL ES 1.1 vs. 2.0 – conditionally build for ES versions
  • In the iPhone display system core animations acts as iPhone display manager and layers are the building blocks
  • Understanding layering, CALayer and CAEAGLLayer, and how layers are animated and composited atop other layers, is fundamental to ES development
  • Test with Animation Instruments to flash updated layers and determine how to best optimize performance based on compositing views
  • Avoid pitfalls in loops with NSTimer by using CADisplayLink instead
  • Optimize render loop by limiting frame rate and drawing on demand
  • Xcode OpenGL ES template contains defensive code that is redundant if you’re not using multiple contexts and color buffers and such
  • Build against ARM based on whether you need floating point maths or not
  • Invest time in reading chipset specs, and the OpenGL ES specs in full

Talk 5: Testing and Debugging

The average iPhone application isn’t known for stability, and this appeared to be one of the most popular talks. I assume most developers attended already understanding the bulk of the material but thinking that even if they gleaned only one meaningful insight or tip the talk would be worth it.

That was my reasoning anyway, but it turned out that the very nature of having the full debugging and QA cycle explained by an Apple developer was reassuring and instructive, and cast some much-appreciated context to the regularly painful process and somewhat disconnected documentation. I didn’t take many notes for this reason, though I’d recommend the talk to others attending the World Tour simply for its walkthough value.

  • Beta Testing – the full process of creating a build for beta testing was covered and there don’t appear to be any special steps or tricks on top of what’s commonly known
  • Make sure to get crash logs from testers; the logs can be symbolicated by organizer effectively providing you with a backtrace of the crash
  • The three most common types of crashes are: low memory, application errors, and timeouts
  • iTune Connect allows you to see crash logs submitted by customers to be the App Store
  • Springboard will always be shown in the logs to consume the most memory, but that’s just a representation of your app

TALK 6: Performance

This turned out to be my favorite talk of the conference. There was a tremendous amount of material covered so the pace was galloping and, as my friend Arthur observed, although it was the end of a tiring day we left energized and awoken. It’s fitting anyway for a talk on performance to speed along at a hurried clip. Of all the talks this was also the one that made me most want to get back into my code to refactor suspected mistakes.

  • Use shark and instruments for profiling speed
  • Memory concerns are fine to test in the simulator
  • But timing or GFX or any hardware involvement should be tested on a device, not only the simulator
  • Drawing optimizations
    • call setNeedsDispaly
    • check the rectangle passed to drawRect
    • mark views as opaque
    • invalidate timer before creating new timer
    • use PNG files as preferred image type
  • ScrollViews
    • use opaque views
    • avoid allocating views during scrolling because allocating views is expensive
    • reuse table cells
    • collapse cell view hierarchy with lots of views, turning a parent uiview with many subviews into one big view with no subviews and use custom drawing routines for top performance
  • Application launch
    • consider user’s immediate needs and load to that
    • make app launch and quit fast
    • load data lazily
    • use small nib for initial UI that is presented to the user
    • don’t perform network operations or expensive operations in ApplicationDidFinisLaunching
  • Memory usage
    • use thumb if possible (floats), thumb 2 / arm if not
    • smaller executables (not bundles) make a difference to launch time since the app exe is loaded to memory
  • Static memory
    • disable in compiler options
      • avoid c++ exceptions
      • avoid runtime type identification (RTTI)
    • prefer direct allocation over convenience methods
    • release directly over autorelease
    • declare properties as nonatomic
    • image and layers might themselves be small, but could have huge backing store
    • every uiview has a CALayer backing it
  • Dynamic memory
    • 4b of data per pixel, RGBA, so big images have exponentially worse performance when they must be decompressed
    • Load UIImages with imageNamed for small, frequently used images; the method is also used by initWithNibName, and automatically caches uncompressed images
    • Load UIImages with initWithData or initWithDataOfMappedFile for large files, and release such images on memory warnings
    • Load UIImages with initWithContentsOfFile for all other (normal) circumstances
    • Use NSAutoReleasePool, especially in loops
  • Technical Q&A

    As a final note, throughout the day those speakers who weren’t presenting at the moment were available for one-on-one Q&A and code review. I managed to grab a slot in order to pose a question that has recently demanded my attention: how, when using a UIScrollView, and zooming in, scrolling to a corner, and zooming back out, can you prevent artificial margins appearing on two edges of the contents?

    The answer: you can’t. However, it’s known, noted, and the SDK team would like to fix the problem. Nobody there thinks this is an acceptable UI design. :)

Tools of the Trade 2009

Posted October 13th, 2009 in Uncategorized by Travis

Mike Gunderloy posted his annual roundup of development tools, prompting Ruby Inside’s Peter Cooper to issue a call for field reports from the rest of the Rubyist operatives out there. Here’s my contribution to this reasonable meme.

Hardware

13″ 4GB Macbook Pro, circa Oct 2009 – running Snow Leopard and Windows 7, this aluminum savior is the anointed godhead of my development world.

17″ SAGER NP9262 (Clevo D901C) – the Apollo to the Macbook’s Adonis, this serves as my “power incarnate” machine. It wears Windows 7 handsomely, and will happily execute any resource intensive tasks I send to its gallows.

Dell 24″ Widescreen Monitor – Has saved more alt/tab keys than I care to calculate.

Mobile Edge Alienware Deluxe Backpack – it took a lot of restraint to put down my loathing for the garish-boutique-markup-vendor of a company and pick up their product, but Alienware carries one of the nicest laptop bags for bending spacetime to accommodate my 17″ Sager, complete with a bivouac’s worth of pouches and harnesses.

Dell XPS M170 – a venerable hero of bygone days, XPS remains in active service running Windows XP SP3 to surprisingly good effect. Trustworthy and redoubtable, measured as the balance of power and portability, and bearing a warranty that allows me to summon field medics!

24″ iMac – Media and not much else. Media and testing.

Backups

External 1TB Seagate drives – Handles everything from automated backups to disc images to source control. Almost as reassuring as RAID, with a bit more administrative overhead.

Various cloud-hosted SVN/Git repositories – Source control repositories are in my opinion the most natural location to “backup” and “restore” from, be it locally or in magical clouds dappled with unicorn glitter.

Synch between machines – An informal but nonetheless practical backup that comes as a consequence of frequently switching computers.

Software

Firefox – I don’t think I use as many FF plugins as most developers, but those I do I find to be indispensible.

  • FireBug – Especially with all the Javascript development I’ve been doing in the last year, FireBug continues to reaffirm itself as one of the most important tools at a web developer’s disposal, and has attained a damn-near-ubiquitous presence in our community.
  • Adblock Plus –It turns out that nobody wants to look at ads, no matter how important they are to a stranger’s business model.
  • ColorZilla – Pure convenience plugin for ganking color schemes from attractive pages.
  • CSSValidator – First step in CSS bug hunting, conveniently in plugin form.
  • PageValidator – Another convenience plugin, helps with developing on FF and testing outwards.
  • JSONView – Beautifully formatted JSON output a la the colored, indented XML we’re accustomed to.
  • FireFTP – Integrated FTP for convenience, though I usually run FileZilla.
  • YSlow – I can guess a site’s YSlow score in my head with reliable accuracy now, but it’s still an effective sanity check.
  • Greasemonkey – Testing in the browser is *painful* and Greasemonkey eases this pain.

XCode – Getting better with every release, and the 10.6 build made some huge improvements.

Visual Studio 2008 – I don’t do enough .NET work these days to upgrade to 2010 yet, but VS remains my favorite IDE on account of its richness and excellent debugger. I still open it on a weekly basis for the Javascript debugger alone.

NetBeans – Probably the most used and abused software on my system after the browsers, NetBeans is a like a whole pantheon of gods for platforms I’m working with and has rightfully become my daily place of worship.

TextMate – A solid “DE”, it’s proven itself quite serviceable for Rails development with a earnest, supportive community striving to ensure, say, that we never run out of attractive color coded themes.

Mac Terminal– CLI power is liberating and arcane and productive like six days of God!

Photoshop CS4 – as only an amateur digital artist, I gravitate to Photoshop not for its power but because of the wealth of instructional information available. I started using Photoshop 4 in high school and the mostly awful Adobe has kept me as an impressionable customer since then.

Paint.NET/GIMP – Perfectly serviceable alternatives to Photoshop that I’m starting to come around to more and more. My GFX program of choice now depends on whatever computer I happen to be using.

MS Excel – The best software ever published by Microsoft and consummate spreadsheet tool. I use it daily in one sundry capacity or another.

Cygwin – For terminal goodness on Windows.

HeidiSQL – Stable, quick, lightweight database interface that just works and never hangs.

FileZilla – I’ve been using this for longer than I can remember for all my FTP transferring needs. If it’s not the best of breed, I wouldn’t know because I’ve never had a complaint that pushed me toward its rivals.

Git/msysGit – The DVCS of the gods; and mortals too, since this is probably one of the biggest software influences on how I work that I’ve adopted in the last few years.

SVN/Tortoise – And yet I also maintain a lot of older and perfectly operable Subversion repos, for which Tortoise is winning interface, though I find myself using SVN more for document management these days than code versioning.

TiddlyWiki – A potent cocktail of brain map, getting things done, project management, scratch paper, and other assorted miscellany that I’ve tried everything from MS Notes to .txt files to manage. TiddlyWiki is my ideal implementation of this bizarre, nebulous workspace.

KeePass – Everyone has a password manager, and KeePass is ever-sagacious gatekeeper to whom I entrust the keys to my world.

Truecrypt – Excellent security, and comforting fixture of my life the more and more I find myself traveling abroad.

PuTTY – For when I must adventure into the frightful lands of Telnet.

Growl – Eternally straddling that line between informative and intrusive.

Jing/Camtasia – For recording demos, screencasts, tutorials, whatever.

Adium/Digsby – Covers 99% of my communication needs.

Skype – And of course, Skype is the other 1%.

Hosting

Dreamhost – A compelling balance between affordability and “unterrible” support, even if the servers are habitually slow. Serves this blog and some other middle-ground needs of mine.

1and1 – Still hosting here due the uncompromising mediocrity of a company whose servers have never been worse or better than “okay”.

Pair.com – Most responsive technical support I’ve ever received from a hosting company.

Heroku – I’ve taken to pushing all new Rails applications I’m building over to heroku, both professional and personal. So far I’ve been very, very, very happy with everything from one-click deployments to their knowledgeable staff.

Web Service API Design By Example

Posted October 7th, 2009 in Uncategorized by Travis

API design is one of the more important skills possessed by web developers today. The reason behind this fact is that web applications are increasingly unable to live in isolation while at the same time providing the scope of features and web awareness users have come to expect. Developing for the web means, to a greater or lesser degree, being a productive citizen in a kingdom of online data exchange; and whether by formal engineering or organic growth, this means building an API.

But API design isn’t limited to pursuing the affable integration promises of the open web. Understanding the power of an API in relation to application design is just as valuable to business needs as sharing data across services is to user desires. The power, in fact, stems from the same source as every other factor in systems design: managing complexity.

Unless you’re writing a Turing Complete API then your guiding objectives will be to simplify the intricacies of your application down to a suitable input/output abstraction, and describe the application’s behavior with as natural and predictable language as possible. The API is a white box with many different black boxes inside. Its design should generally represent a boundary point after which any further encapsulation of logic would require unproven assumptions, or else seriously violate the separation of concerns, and a boundary point before which any more flexibility and power requires special operating knowledge of the system because the API is too dense to be self-descriptive.

This is a razor-fine line and easy to slip from when moving within the system, but an API that’s clear and approachable to strangers serves in an equally valuable capacity at documenting behavior and intent. In the same way that TDD promotes the ancillary yet no less important benefit of forcing the developer to conceive of their code’s structure, methods, organization, etc. in terms of real-world usage, an API can also provide a sort of blueprint allowing for a theoretical rehearsal of the design impossible with strict bottom-up development.

None of these notions are particularly controversial or open to much debate; the tension is drawn between the gap of theory and practice, and for that I want to look at some examples from some of today’s prevailing web services. Reflecting on the conclusions reached by web leaders should provide us with insight into our own design efforts, and read the collective pulse of the open web.

Twitter

http://apiwiki.twitter.com

Positive: Twitter has become the gold standard in API adoption among the developer community, and much of this owes to a lucid API design. The organization of methods under loose “resource” groupings is easy to understand, and the individual method documentation is clean, forthright, and contains exactly the information you would expect including usage examples, authentication details, parameter constraints, response data, and more. What’s more, you can usually expect the resources to answer in XML, JSON, RSS, and Atom formats, another area Twitter stands out.

The documents do an excellent job of providing implementation these details, alongside top-level summaries of the general API design and conventions.

Twitter’s greatest accomplishment, though, is managing information overload from the potential expressiveness of the system. To point: you can almost immediately write code to begin talking with the API after reading only a few pages of documentation, and that’s as good a litmus test as any.

Negative: There’s not much to dislike about Twitter’s API and documentation, but one uncomfortable area is the quasi-RESTful model it implements. While the resource structure is clear, Twitter’s naming conventions are arbitrary and depend on the developer’s learning custom vocabulary for utility methods. This is a challenge failed by most APIs, though, and while Twitter is exceptional in many positive ways, here the API is no exception.

 

Basecamp

http://developer.37signals.com/basecamp/

Positive: The Basecamp API and documentation matches the 37 Signals adherence to simplicity, and you can familiarize yourself with the whole system in short order. The introduction covers implementation details like status codes, content types, and file uploads. If there’s anything to like about the API, it’s that you can learn it over a cup coffee.

Negative: For all its virtues of simplicity, the API lacks the cohesion found, say, in the only slightly more verbose Twitter API. The resources are well-named but the URLs are subject to unguessable nesting and inconsistent patterns. Searching and filtering data is not easily done, and the usage notes for those few methods which permit manipulation are hastily explained. As far as formats, only XML is supported.

 

Facebook

http://wiki.developers.facebook.com/index.php/API

Positive: Facebook’s API sits at the opposite end of the design spectrum, with a rich, intricate framework behind it. The power of that framework and data stores gives the API its strength and more than justifies its complexity.

Facebook deals with this by progressive disclosure in the documentation, and a small ecosystem of sub-APIs for permissions, application authorization, database queries, a markup and widget language, and many things in between. If the learning curve is steep, the documentation does a respectable job of guiding the developer down through specific methods and use cases as needed, and everything is well-organized, indexed, and linked.

Negative: The downside of the breadth, depth, and power is obvious. To use the API you must essentially learn a new framework, along with its own pitfalls and nuances. The entire nature of the design revolves around specialist knowledge, with heavily parameterized methods and enough usage exceptions to make casual experimentation and rapid development less inviting than alternative APIs.

 

Flickr

http://www.flickr.com/services/api/

Positive: Flickr supports a tremendously flexible API with an impressive number of both response (REST,XML-RPC,SOAP, PHP, JSON) *and* request (REST,XML-RPC,SOAP) formats. The documentation is descriptive, consistent, and concise, and the brief overviews on dates, encoding, tags, urls, and suchlike make further explanation in the method unnecessary.

Like many APIs, Flickr’s authentication is the most complex area of the API, which Flickr eases with instructive guides to authenticating from the web, desktop, and mobile platforms. The method naming is sensible, if not intuitive, while the method grouping is generally both.

Negative: Between support for SOAP and RPC calls, and a prescriptive approach to method naming conventions, Flickr demands slightly more special understanding than we would prefer, but with this as its greatest deficiency I find it to be on the whole well designed.

 

Digg

http://apidoc.digg.com/

Positive: Unlike other APIs here, Digg is essentially an endpoint to a single resource: stories. The heavy parts of the API revolve around filtering the stories on multiple criteria, as can be seen from the route index. The API’s basic concepts are standard enough, and while Digg expects its consumers to cache and chain data requests to prevent system overload, the documentation clearly explains the request patterns intended for talking to the API with these constraints.

Data can be requested in a commendable variety of formats, from XML, JSON, Javscript, or serialized PHP. Each documented method also contains plenty of illustrative example response data.

Because of the focus on stories, there are fewer API methods and each method call can be customzied and purposed with a variety of optional arguments. The documentation systematically explains the exceptions and conditions under which parameters are used, and the design gives far more power to arguments than to routes, in other words more RPC than REST.

Negative: On the other hand, the argument-laden nature of the API methods require careful review and implementation, overhead that could have been avoided by consolidating the filtiering logic either in the documentation or the API itself. This also makes it difficult to grasp the full range of the API without serious review of all of the its corner cases.

The flood controls on API requests are explained well, but irritating to accommodate in contrast to something like Google Data which has a more passive approach to imposing usage restrictions on its consumers. Further requirements like, for example, a User Agent header, make Digg’s API feel less inviting and fault-tolerant than alternative designs.

 

eBay

http://developer.ebay.com/developercenter/rest/

Positive: eBay is firmly in the RPC design camp, and frankly, aside from desirable auction data, there’s not much to recommend the API. If the documentation seems exhaustive – and it most certainly is, see the guide to REST, for example – then it’s an honest emmisary of the beast of a system which sits behind an ostensibly friendly API frontend. Although there are detailed code samples and a development sandbox, these are more necessary complications than any kind of extra, facilitating support. The API does support XML, SOAP, Name Value, and JSON output, though, which makes sense beside the seeming design ambition of leaving no possibility unimplemented.

Negative: Challenges for the developer abound in the API. Issues in the documentation from PDF-only guides, broken hyperlinks, poor IA for a system large enough to clearly deserve it, inconsistent method implementation and naming schemes, duplication of functionality, you name it! If there’s a potential roadblock to eBay integration, you’re sure to find it somewhere in this API.

The documentation is a mix of structured, heavily formatted schema descriptions with confusing conventions on when to bold/italic, change header size, change fonts, etc., and writing that ranges from conversational to clinical in enough voices to give the impression of design by committee. The jargon required to follow the nine-plus API products (searching, shopping, matching, feedback, trading, etc, etc) is everything from dependency and monkey-patching, and nothing from an open, agnostic web. Individual method writeups are an embarrassment of riches with ample reference but no progressive disclosure and little indication as to why one thing is more important than another, or suddenly described at a different level of abstraction than the rest of the page, even digressing into legal or business discussion in the middle of a technical section.

Perhaps the most damning complaint is that there is no suitable top-down entry point for acquainting yourself with design of the API and the extent of its power, no way to understand the goals and constraints of the system or even a clear idea of what exactly it’s modeling.

 

Technorati

http://technorati.com/developers/api/

Positive: Technorati answers the same needs as Digg, but does so with a considerably more elegant (if a bit less flexible) API. In fact, it’s elegant enough that no introductory document is needed, and there are only a handful of methods you need to access its full functionality. Responses are formatted as XML or RSS, and further documentation is provided for setting up custom pings to the service.

Negative: Although the superficial depth of the API makes for swift development, API influence is largely relegated to reading, not manipulating, data. And while a simple system might otherwise lend itself to descriptive structure and resource names, Technorati is not much better at its taxonomy that much more complex APIs.

 

Google Data

http://code.google.com/apis/gdata/

Positive: The Google Data API is difficulty to appraise simply because it’s so diverse. Google Data imposes the governing design for dozens of Google services that each depart from, reduce, or extend the Base API to varying degress. While there is a solid conceptual overview, introductory video, and plenty of examples and framework-specific libraries, the price of admission to Google’s power is study and specialization.

In addition to the resources mentioned above, Google’s documentation acquits itself well in presenting information on the daunting scope of functionality and flexibility found in its APIs. Progressive disclosure, intuitive taxonomy of services, methods, and overall IA, and concise, consistently formatted documentation are the winning conditions that keep the API accessible and friendly.

Negative: It’s hard to fault google for complexity while understanding the design challenge, and especially in consideration of the fact that large but comparatively smaller APIs than Google (e.g. eBay) fail much more spectaularly at intelligble design and documentation.

 

Netflix

http://developer.netflix.com/docs

Positive: The Netflix API is very RESTful with a convincing explanation (and diagrams) behind how resources are organized and manipulated, and some clever tricks like an “expand” parameter for retrieving associated model data.

Documentation takes an instructional approach, walking through the various aspects of the API with perhaps too much depth at places, a distraction otherwise compensated for by the outstanding layout, formatting, example code, and succinct writing style. The extensive annotated screenshots are well chosen and never seem to clutter your search for information. A more formal reference for the API is available as well, where you’ll find the Netflix design to be quite respectful of RESTful principles. There’s even a lightweight Javascript API for basic Netflix actions.

In fact, the documentation is so excellent, if a little verbose, only because it reflects a tight, descriptive API design. In this case,I don’t mind the API writers erring on the side of explanation because it’s always clear how what you’re reading relates to the API as a whole.

Negative: Netflix stands out among rival APIs, both in thoughtful documentation and technical design. Perhaps its weakest point is the use of OAuth for authentication, considering the added complexity. XML is also the only supported format outside of their Javascript API.

 

Activity Logs and Friend Feeds on Rails & pfeed

Posted August 20th, 2009 in Uncategorized by Travis

Friend networks and activity feeds are mainstay features of social media applications, and designing an implementation that won’t scar your code with the complexities of bidirectional logic and messaging queues is never easy.

That should be enough to send the thoughtful developer looking for a giant’s shoulders to stand on before climbing the task themselves, but in the case of Rails, otherwise known for the fecundity of its plugin community, there is no compelling solution stack to raise us up. That is, not until Abhishek Parolkar released pfeed:

A rails plugin that allows you to create extensible log of activity

I’m going to walk through the process of setting up pfeed in your application. The sample code I’ll use is drawn from my own implementation of the plugin as well as pfeed’s github pages, including:

Although I’ll be expanding on the code samples, you’ll still want probably want to consult the original documentation at some point.

Requirements

In my case, there were several requirements driving plugin selection, as well as the rationale behind even seeking a plugin in the first place instead of building the functionality from scratch.

  1. Should configure logging, not manually call a log method
  2. Should store additional arbitrary log data in easily retrievable way
  3. Should be able to log to/for any model, not just “users” (i.e. log a service, or report TO a service)
  4. Should be able to differentiate between log types/categories
  5. Should scope log activity to user or user groups
  6. Can easily be globally disabled
  7. Should work across model associations to log “nested” activity
  8. Won’t clash on models with many preexisting Active Record callbacks

I’m looking for a lot of flexibility here. And while I mention “logging” quite often, there’s nothing about “friendships” or “friend feeds”. That’s because the “logging” and “feed” system would be serving my application in a number of ways, and what I needed was something that could perform generic logging tasks which I could then arbitrarily interpret for various user and system activity tracking scenarios. I could have taken inspiration from somewhere like Insoshi but I wanted a solution that didn’t carry the furbelow of social networking.

Installation & Setup

Install the plugin from git…

script/plugin install git://github.com/parolkar/pfeed.git
rake pfeed:setup

Installation will, among other things, add two tables to your database and install the Inflectionist plugin, which is used to add linguistic sugar to the feed messages.

   create_table :pfeed_items do |t|
     t.string  :type
     t.integer :originator_id
     t.string :originator_type
     t.integer :participant_id
     t.string :participant_type
     t.text   :data
     t.datetime :expiry
     t.timestamps
   end

   create_table :pfeed_deliveries do |t|
     t.integer :pfeed_receiver_id
     t.string :pfeed_receiver_type
     t.integer :pfeed_item_id
     t.timestamps
   end

After that, you’re ready to configure your models. For this example we just need some users, friendships, and some models on which to track user activity.

NOTE The majority of the following model code is devoted to managing friendships, but it is not necessary to follow this design in your own friend models. pfeed will group logs for you on any Active Record association; there is nothing special about friends. The following user model could just as well be receiving a feed from comments (since it’s just another association).

User Model

class User < ActiveRecord::Base
  has_many :comments, :dependent => :destroy
  has_many :friendships, :dependent => :destroy
  has_many :friends,
    :through => :friendships,
    :foreign_key => 'friend_id',
    :class_name => 'User' do
    def active
      find(:all, :conditions => ['completed_at IS NOT NULL'])
    end
    def pending
      find(:all, :conditions => ['completed_at IS NULL'])
    end

  def request_friendship_with(friend_id)
    friend = self.friendships.detect {|f| f.friend_id.to_s == friend_id.to_s }

    if friend.blank?
       friend = Friendship.new(:user_id => self.id, :friend_id => friend_id)
       return friend.save
    else
      return false
    end
  end

  def complete_friendship_with(friend_id)
    friend = self.friendships.detect {|f| f.friend_id.to_s == friend_id.to_s && f.completed_at.blank? }
    unless friend.blank?
      friend.touch(:completed_at)
      friendship = Friendship.find_by_user_id_and_friend_id(friend_id, self.id)
      friendship.touch(:completed_at)
      return friendship.id
    end

    return false
  end
end

Friendship Model

class Friendship < ActiveRecord::Base
  belongs_to :user
  belongs_to :friend, :class_name => 'User'

  validates_presence_of :user_id, :friend_id
  validates_numericality_of :user_id, :friend_id

  after_create :ensure_complementary_record_exists
  after_destroy :ensure_complementary_record_is_destroyed

  named_scope :include_users, :include => [:user, :friend]

  protected
  def find_complementary_record
    Friendship.find_by_user_id_and_friend_id(friend_id, user_id)
  end

  private
  def ensure_complementary_record_exists
    Friendship.create!(:user => friend, :friend => user) if find_complementary_record.blank?
  end

  def ensure_complementary_record_is_destroyed
    if complement = find_complementary_record
      complement.destroy
    end
  end
end

Comment Model

class Comment < ActiveRecord::Base
    belongs_to :user
    belongs_to :parent, :polymorphic => true, :counter_cache => true
end

Tables

create_table :friendships, :force => true do |t|
   t.integer :user_id, :null => false
   t.integer :friend_id, :null => false
   t.datetime :completed_at

   t.timestamps
 end

create_table :users, :force => true do |t|
   t.string :name
   t.string :password
end

create_table :comments, :force => true do |t|
  t.integer :user_id, :null => false
  t.integer :parent_id, :null => false
  t.string :parent_type, :limit => 64, :null => false
  t.string :body, :limit => 2048, :null => false
end

pfeed Configuration

The concept behind pfeed is simple. There are models which receive feeds, and those which emit them, and in some cases a model may do both. Integrating pfeed revolves around (1) configuring your models, (2) deciding which methods you want to log, and (3) adding any additional information onto the default data which pfeed collects.

Feed Receivers

To make a model a feed receiver, you add the receives_pfeed macro call somewhere near the end of the class so as to avoid association collisions.

class User < ActiveRecord::Base
   . . .
   receives_pfeed
end

When you register a model as a receiver, it gains a pfeed_inbox association, so that you can call, say, @user.pfeed_inbox to return a collection of feed items. This is the primary way you’ll retrieve feed information – through the context of a receiver. In this example it’s a User model, but it really could be any sort of “listener” model under which you need to scope your logs.

Feed Emitters

Registering the models to emit feed requires a little more customization, and you do it with another macro call, emits_pfeeds, to which you pass a hash of methods to act as log triggers, and a hash of models who will receive feeds of those logs. It looks like this:

class Comment < ActiveRecord::Base
    . . .
    emits_pfeeds :on => [:create] , :for => [:all_users]
    def all_users
      User.all
    end
end

The :to argument

This hash will accept any method on your model. Sometimes you’ll want these to simply be native Active Record operations, like :create or :update_attribute, but sometimes you’ll want to use your model’s own methods, which we’ll do in a second when logging user friendships triggered by the example User model’s complete_friendship_with.

The :for argument

The :for hash accepts a method for defining the feed’s “audience”, where audience is some Active Record model or collection. The plugin has two native methods for this, :itself and :all_in_its_class. They are simply defined:

def itself
  self
end

def all_in_its_class
  self.class.find :all
end

If you look back you’ll see that I defined an all_users method for the Comment model. Defining recipients for the feeds in such a way provides a lot of power and flexibility in filtering how and when your feeds are broadcast.

With the preceding code samples, we’d be logging every comment that was created, and permitting those logs to be retrieved by users.

Logging Friendships

Say we want to log whenever a user’s friend becomes friends with another user, and then show it on a feed like this: travis became friends with parlokar about 6 minutes ago.

First, since our user will be both receiving and emitting feeds, we add both pfeed macro calls to the model.

class User < ActiveRecord::Base
   . . .
   has_many :friends
   . . .
   emits_pfeeds :on => [:complete_friendship_with], :for => [:itself ,:all_in_its_class, :friends]
   receives_pfeed
end

There is a lot of wonderful black magic happening here, but essentially this code translates to say that feeds triggered by the complete_friendship_with method will be associated with the “friending” user, and that when such users appear in the :friends association of another user, that user can see these feed items.

Between a model’s ability to generate feeds for actions itself takes (e.g. complete_friendship_with) as well as actions it takes against other models (e.g. Comment.create), pfeed provides an enormous amount of flexibility in structuring your log system.

Feed Item Messages

This would be an excellent time to refer to the pfeed documentation on custom feeds. Basically, pfeed will automatically log enough data to create a readable feed message that can be reconstructed from the user association in combination with the triggering method’s name. Feed items will generally look like this:

Travis updated attribute Email about 1 minute ago
Travis created comment about 10 minutes ago
Travis completed friendship with about 30 minutes ago

The last item should draw your attention since it looks quite incomplete without, say, the friend’s name. pfeed has a sort of templating system for determining the data saved by a log, and if you wish to expand upon the defaults, you’ll have to create a feed model for each of the actions for which you want more data.

First, create a subdirectory named “pfeeds” in your application’s models directory. Then, you’ll create models for each of the methods you need to capture custom data from, inheriting from the PfeedItem model.

The directory structure will look like this:

– models
–– pfeeds
–––– comment_created.rb
–––– user_completed_friendship_with.rb

And the feed models will look like this:

class Pfeeds::UserCompletedFriendshipWith < PfeedItem
def  pack_data(method_name, method_name_in_past_tense, returned_result, *args_supplied_to_method, &block_supplied_to_method)
     self.data = {} if ! self.data

     friendship = Friendship.include_users.find(returned_result)

     hash_to_be_merged = {:friend => friendship.friend.name }
     self.data.merge!  hash_to_be_merged
     super
  end
end

In this example I want to capture the name of the friend with whom the logged user completed a friendship, so I define a field (“:friend” in self.data arguments) which will then be serialized into the data feed.

When you’re working with a feed item, you can then retrieve the custom data like so:

friend_name = @feed_item.data[:friend]

Summary

Now that we’ve covered each of the four corners of pfeed, so to speak, we can summarize integration as follows:

  1. Define feed receivers with receives_pfeed
  2. Define feed emitters with emits_pfeeds
  3. Define emits_pfeeds :for methods to scope logs
  4. Create PfeedItem models for capturing custom log data

I started this post with a list of requirements, and the various hooks pfeed gives you provide answers to all of them. The extent to which you configure and customize your logging system is up to you, and for me this was the real power of the plugin. Pfeed goes to the exact boundary of common logging functionality (the code I want to avoid writing myself if possible), and yet stops precisely before incurring design commitments that might clash with my application, leaving the right amount of “freedom with help” that is the hallmark of a valuable plugin.

So what are you waiting for? Go git it. . .