Andrey Shchekin Sat, 13 Apr 2013 23:16:21 +0000 en-US hourly 1 Multiple submit buttons with ASP.NET MVC: final solution /2010/03/15/multiple-submit-buttons-with-asp-net-mvc-final-solution/ /2010/03/15/multiple-submit-buttons-with-asp-net-mvc-final-solution/#comments Mon, 15 Mar 2010 18:00:00 +0000 /index.php/2010/03/15/multiple-submit-buttons-with-asp-net-mvc-final-solution/ Most people coming to ASP.NET MVC tend to have similar problems. Most of them are related to refreshed understanding of HTML+HTTP limitations. Some are mitigated by ASP.NET MVC (checkboxes), but a lot require custom solutions.

One of the more common problems I get asked about by every second person learning MVC is how to make a form with two independent submit buttons, where both should submit the same data, but a have a different processing logic. For example, form may have a Save Draft button and Publish button.

Until now, I always said that it is possible, Google/StackOverflow and find out. But I just had to build such kind of thing myself, so I finally took some time to find/build the final solution.

So, let’s look at the solutions available online.

StackOverflow question “How do you handle multiple submit buttons in ASP.NET MVC Framework?” is the starting point, but it does not provide a good solution for the situation where you need to send same data for both buttons, except the solution to switch by button name, which is duct-taping.

Post “ASP.NET MVC — Multiple buttons in the same form” by David Findley is much more interesting, since his AcceptParameterAttribute is very similar to my solution. However, this has several (small) shortcomings: first, you have to specify what is actually an action you want to do in an attribute. So even if you name your action “SaveDraft”, you will still need to specify AcceptParameter(Name=“button”, Value=“saveDraft”). Another thing is need to put [ActionName] on your actions, which is understandable, but a bit confusing for people who do not yet know the idea.

So, I wanted to build the solution that would require at most one attribute, and where the name of action method corresponds to the attributes of the button. Also, since <input> value is the thing being shown in the button, and <button> value has issues in IE, I decided to go with name attribute and ignore value completely (which seems to be consistent with David Findley’s commenters).

Now, the solution. Basically, instead of using ActionMethodSelectorAttribute, I am using ActionNameSelectorAttribute, which allows me to pretend the action name is whatever I want it to be. Fortunately, ActionNameSelectorAttribute does not just make me specify action name, instead I can choose whether the current action matches request.

So there is my class (btw I am not too fond of the name):

public class HttpParamActionAttribute : ActionNameSelectorAttribute {
    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo) {
        if (actionName.Equals(methodInfo.Name, StringComparison.InvariantCultureIgnoreCase))
            return true;

        if (!actionName.Equals("Action", StringComparison.InvariantCultureIgnoreCase))
            return false;
        var request = controllerContext.RequestContext.HttpContext.Request;
        return request[methodInfo.Name] != null;

How to use it? Just have a form similar to this:

<% using (Html.BeginForm("Action", "Post")) { %>
  <!— …form fields… -->
  <input type="submit" name="saveDraft" value="Save Draft" />
  <input type="submit" name="publish" value="Publish" />
<% } %>

and controller with two methods

public class PostController : Controller {
    public ActionResult SaveDraft() {

    public ActionResult Publish() {

As you see, the attribute does not require you to specify anything at all. Also, name of the buttons are translated directly to the method names. Additionally (I haven’t tried that) these should work as normal actions as well, so you can post to any of them directly.

There is some room for improvements (hardcoded “action” as a default action name), but in general I’m satisfied with this solution.

/2010/03/15/multiple-submit-buttons-with-asp-net-mvc-final-solution/feed/ 89
Includes in Boo /2009/12/13/includes-in-boo/ /2009/12/13/includes-in-boo/#comments Sun, 13 Dec 2009 18:33:23 +0000 /?p=114 This post assumes you know what Boo is (a rather useful .NET language).
I wanted to write a more descriptive post on Boo and other scripting approaches, however this post is more specific.

Ok, so if you know what’s Boo is about, you probably know that there is no official way to include one boo file into another. There are several approaches that achieve similar results, like Ayende’s import file from. However, they are all lacking when using Boo as a DSL or a scripting tool.

They are based on pre-compilation of referenced file, which has a side-effect of creating unnecessary assembly and losing access to global variables from the original file. Now, if I actually wanted to import global variables (for example, as a configuration in a build-file DSL), I was out of luck.

Fortunately, Boo has a rather useful macro system, which allows classes named ‘macros’ to affect the parsed representation of the program. So the question of include is just a question of building a macro that injects parse tree of referenced file into referencing one. I have just finished a basic include implementation, which can be found here:

  1. IncludeSupportStep.cs
  2. IIncludeCompiler.cs

(note that Phantom project itself is 1. awesome, more on that in future posts and 2. not written by me)

IncludeMacro is the macro, and the IncludeSupportStep is used to inject IIncludeCompiler implementation into the list of services provided by CompilerContext so it will be available to the macro. Now, what is IIncludeCompiler? It is a wrapper for a basic Boo compiler which should have only Parse steps in the pipeline + any steps provided by the DSL factory (except ImplicitBaseClassCompilerStep).

Having IncludeSupportStep means that this functionality is not directly available through boish/booi/etc, which is a pity, but is probably not hard to fix.

The syntax for the include is pretty simple:
include ''

I have chosen ‘include’ instead of import to make the difference obvious and to simplify the syntax for use in DSLs. Not sure if it is the best choice, though.

It is interesting that in comparison with Ruby/etc, this is a purely static include, so wrapping it in any control flow blocks achieves nothing.

/2009/12/13/includes-in-boo/feed/ 0
Projecting unmapped Ids with Linq-to-NHibernate /2009/11/05/projecting-unmapped-ids-with-linq-to-nhibernate/ /2009/11/05/projecting-unmapped-ids-with-linq-to-nhibernate/#comments Thu, 05 Nov 2009 17:46:50 +0000 /?p=80 There are situations when you do not need to get the fully tracked entities using NHibernate — you know you wouldn’t ever edit them and need minimum overhead for this specific scenario. One example is AJAX auto-completion. On the other hand, if you are as obsessed with architectural purity as me, you probably do not have Id properties in your entities, since they are artefacts of (relational) DBs.

So there is the question: in UI layer we want to do

repository.Query().Select(x => new ListItem { Key = x.Id, Name = x.Name })

But there are no “Id” properties in our entities. How can we do this (without returning to the dark ages of untyped criteria)?

There is a very simple (and working) answer. Let’s start with how I do it for individual entities. When I need a key/id in the UI to identify the entity between requests, I use repository.GetKey(entity), which internally calls session.GetIdentifier(entity). Simple and not intrusive into domain logic. Now,

repository.Query().Select(x => new ListItem { Key = GetKey(x), Name = x.Name })

is obviously impossible, since HQL/DB can not understand GetKey call.

Ok, so the solution is to pre-process the call before Linq-to-NHibernate and replace GetKey call with reference to fake property named “id”, which is a magic name NHibernate understands as identifier reference. Linq-to-NHibernate even provides public expression visitor, so it was trivial to create KeyMethodToIdRewritingVisitor (the fake PropertyInfo took most effort, which had to have some stuff to fool Expression.Property).

You can get resulting code below.
It is not perfect, but it works and flaws are really easy to polish out.

  1. Repository
  2. KeyMethodToIdRewritingVisitor
  3. KeyEnabledQueryProvider
/2009/11/05/projecting-unmapped-ids-with-linq-to-nhibernate/feed/ 0
Custom mapping for NHibernate.Search /2009/10/26/custom-mapping-for-nhibernate-search/ /2009/10/26/custom-mapping-for-nhibernate-search/#comments Mon, 26 Oct 2009 15:04:03 +0000 /?p=66 Recently I had a need for fuzzy search in one of my projects. Since I was using NHibernate, NHibernate.Search (Lucene.NET-based) seemed like a good choice. However, there was one limitation — NHSearch required custom attributes for its mapping.

That was suboptimal. I had to reference NHSearch from domain entities class, I had to add Id property to my entities to map NHSearch [DocumentId]. Fortunately, it was a free time project, so I decided to take a break from it and fix the NHSearch mapping.

Some days later, the patch was in NHSearch trunk.

So, while there are no new built-in mappings except the attribute one, it is now possible to create a mapping manually. Here is a simple sample mapping that indexes Title property of Book entity:

using NHibernate.Cfg;
using NHibernate.Engine;
using NHibernate.Properties;
using NHibernate.Search.Bridge;
using NHibernate.Search.Mapping;

internal class CustomSearchMapping : ISearchMapping {
    public ICollection<DocumentMapping> Build(Configuration cfg) {
        var bookMapping = new DocumentMapping(typeof(Book)) {
            DocumentId = new DocumentIdMapping("Id", BridgeFactory.INTEGER, null),
            Fields = { MapField<Book>(book => book.Title) }

        return new List<DocumentMapping> { bookMapping };

    private FieldMapping MapField<T>(Expression<Func<T, object>> propertyReference) {
        var property = (PropertyInfo)((MemberExpression)propertyReference.Body).Member;

        var getter = new BasicPropertyAccessor.BasicGetter(typeof(T), property, property.Name);
        var bridge = BridgeFactory.GuessType(property.Name, property.PropertyType, null, null);

        return new FieldMapping(property.Name, bridge, getter);

That’s something that will hopefully get better in time (notice required call to BridgeFactory.GuessType with nulls, that one thing I haven’t yet got to fix).
But this works, and this does not require attributes (and this does not require reflection at all, actually, you can write your own IGetter with any kind of logic).

One more thing — the “Id” parameter to DocumentIdMapping is how the Id field will be named in index, not your identifier property name.
There is also additional parameter to DocumentIdMapping that specifies property name, but in most scenarios it should just work, even if the identifier is not mapped.

To enable this custom mapping, add the following to your nhsearch.cfg.xml or web.config:

<property name="">Sample.Repositories.CustomSearchMapping, Sample.Repositories</property>
/2009/10/26/custom-mapping-for-nhibernate-search/feed/ 7
.NET DI (IoC) Frameworks, Revisited /2009/10/12/net-di-ioc-frameworks-revisited/ /2009/10/12/net-di-ioc-frameworks-revisited/#comments Mon, 12 Oct 2009 15:27:55 +0000 /?p=47 About a year ago I wrote a comparison of .NET IoC frameworks (part 1, part 2), which turned out to be somewhat popular. I was pretty sure it is quite obsolete by now.

However, when Chris Tacke asked me to review OpenNETCF IoC framework (built for .NET CF) I have decided to update the charts on Google Code wiki.

Now I updated all frameworks there to the latest versions. I am not sure I got everything correctly this time, because I didn’t want to spend too much time on this. If I made an error in description of your framework features, just tell.

It is interesting that there are almost no significant changes in tests I have. Changes seem to slow down since the time I was writing the original posts. I think there are two reasons for that: frameworks being feature-complete enough not to need rapid evolution and me missing new features because my original test scope is somewhat obsolete. I am almost sure I will not have time to research all new developments, which is unfortunate.

As I said in a previous post, in addition to the charts, Google Code project has test adapters for all frameworks, which can be used as sample integration code. If there is something I do with your framework that can be done in a better way, tell me. I will also grant write rights on repository to any interested framework authors.

/2009/10/12/net-di-ioc-frameworks-revisited/feed/ 4
Putting Web snapshots to practical use /2008/09/28/putting-web-snapshots-to-practical-use/ /2008/09/28/putting-web-snapshots-to-practical-use/#comments Sun, 28 Sep 2008 10:50:04 +0000 /index.php/2008/09/28/putting-web-snapshots-to-practical-use/ I was always interested if the service that, for example, WebSnapr provides is practically useful. Having snapshots of a linked pages looks very cool indeed, but does not really provide much information, at least for me.

But recently I have found a nice use case for a similar service. For tables in my IoC Frameworks posts, I was using parts of a Zoho spreadsheet embedded through an iframe. However, Google Code wikis do not allow iframe embedding.

The easy solution was to use built-in wiki table markup, but in this case I would lose all color coding and ability to download the whole sheet as Excel. The interesting solution was to embed snapshot of an iframe, as an image. This would also allow me to solve the iframe problem for RSS readers that do not support them.

My original idea was to use Firefox or Chrome (via Chromium project) to do the snapshots, but I have not managed to use the Mono.Mozilla on Windows and I was too lazy to dive into Chromium.

IE was the simplest remaining choice, so using information from the great article of Peter Bromberg, I have built my own implementation of web snapshotting service. The main difference between my snapshots and other services is that my service can actually determine the correct size of the snapshot.

For example, this is a snapshot of my IoC frameworks table:

You can click it to see the source of the snapshot. As you can see, it has correct size (instead of being fixed to 1024×768 or another screen size). On an unrelated note, it is an updated version of framework comparison, now including LinFu (which was updated to pass all feature tests).

In Google Code it looks like this.

Additional interesting usage for this would be iframe-widget embedding to locations that do not support iframes. For example, it is possible to embed a list of RSS feed headers or twitter messages in Google Code using this technique.

You can svn-download source code from Google Code. It is really bare for now, no good error-handling and no resizing support (all snapshots are always displayed in full size). However it may be a good starting point.

Looking at this project, I think that it would be very interesting to have a .NET wrapper for Chrome APIs that would allow anyone to automate Chrome, which probably will make snapshot extraction much faster and not COM-reliant.

/2008/09/28/putting-web-snapshots-to-practical-use/feed/ 8
Programming is complex: HTML5 data-* /2008/09/21/programming-is-complex-html5-data/ /2008/09/21/programming-is-complex-html5-data/#comments Sun, 21 Sep 2008 11:55:40 +0000 /index.php/2008/09/21/programming-is-complex-html5-data/ According to HTML5 spec, every attribute that starts with data- should be ignored by browser and considered a data needed for some scripts. For example:

<ul data-sortable="yes">…</ul>

Does it remind you of something? It is a simplified version of XML namespaces, but limited to non-hierarchical data and with much more probability of data clash.

John Resig, in the comments to his blog post on the matter, says that “The learning curve and failure rate are too high to purely XML-based markup, which is why the data-* attribute exists as a means to implementing this solution”. I completely disagree with the learning curve argument, and this is why:

Programming is complex.

It is easy to forget about the complexity while doing smart new stuff. And new cool things is not what I am talking about. Inventing new thing is easy, since you do not have to think about all limitations of the new ones. Making them work for all occasions, that is what is complex.

It is easy to praise HTML5 over XHTML. It is easy to praise microformats.

But all standards are here for a reason. It is easy to punch Microsoft, however first versions of Google’s Picasa didn’t support non-English text in labels, at all. Current version of Adobe Buzzword does not support Russian text. Current versions of Picasa and Opera do not support Drag&Drop outside of application. I have never seen any of such problems in MS applications. In fact, Visual Basic supported Unicode for ages, while relatively modern web languages stumble on it.

I have already blogged about microformats being a hack. And now I read about BBC removing microformats support due to problems with screen readers and semantics of abbr tag. Is it easy to remember that screen readers must be supported? No. Is it a shiny 2.0 kind-of-thing to think about them? No. But it is one of the multiple things you have to think about when making a wide-used standard.

Adding easy solutions when possible is a great thing. But it wrong to think of programmers as people who can only work with easy solutions. Flexibility is much more important than ease of use, and that’s the lesson that produced, for example, ASP.NET MVC. Also, easy ways should exists as shortcuts for flexible ones, not be orthogonal to them.

And while HTML may be about design, and text processing, and other things, data-* is about programming.

/2008/09/21/programming-is-complex-html5-data/feed/ 6
Comparing .NET DI (IoC) Frameworks, Part 2 /2008/09/08/comparing-net-di-ioc-frameworks-part-2/ /2008/09/08/comparing-net-di-ioc-frameworks-part-2/#comments Mon, 08 Sep 2008 20:41:05 +0000 /index.php/2008/09/08/comparing-net-di-ioc-frameworks-part-2/

Note to RSS users: There are embedded spreadsheets in this posts that Google Reader silently hides. I prefer RSS myself, but for this post Zoho features are very useful and, unfortunately, I see no way to make it usable in Google Reader. Sorry about that.

See also: .NET DI (IoC) Frameworks, Revisited.

Update: Several people have pointed to the fact that some issues mentioned in my post are no longer relevant. I will try to mention it, but in general this post reflects only the versions I have listed in the previous one.

However, Jeremy Miller (author of StructureMap) has pointed to a significant oversight in my review: StructureMap is not limited to static APIs. Since this error is so large and relevant to the reviewed version, I am completely rewriting this part now.

I have finished the previous post with a summary of basic injection/autowiring features. It would be great to say that I will cover all remaining features in this part.

However, it is not possible to review and compare every single feature of all IoC frameworks, and some things (like AOP) I felt to be outside of the scope of IoC functionality. So this review will be somewhat limited, but, I hope, still useful.

4. Lifestyles

As you can see, all frameworks support two basic lifestyles, transient and singletons. In addition to this, all of them have an ability to define custom lifestyles (the specific APIs are named quite differently between frameworks).

I have also added support for custom instance registration to the same table (custom instances are instances built outside of the container and then provided as an implementation of some service interface). This support is also strong in all reviewed frameworks.

The interesting thing that can be learned here is that extensible lifestyles system is a baseline feature for any IoC framework, something that must be supported. This is quite understandable for singletons and transients, but it is nice to see that extensibility is also very popular.

5. Advanced resolution

While the previous section deals with the baseline features, this section contains advanced features that I feel should be a new baseline.

So, what is it all about?

Open generics injection is an ability to register open generic types and receive specific generic types on demand. Basically, it is about registering Component<> as IService<> (not giving any generic arguments), and on request of IService<X> getting Component<X>.

It seems most framework authors tend to agree that is an useful feature, so all frameworks except Spring.Net support open generics injection. I think it is already very near to a new baseline. Thanks to Castle guys for introducing it.

List injection is something I already discussed in a previous post. It is an ability to register several IService implementations, and then get them all through IService[] (IList/IEnumerable/…) dependency. I have found it very useful, and I hoping it would become a new baseline some day.

I was very surprised by StructureMap actually supporting this functionality (I haven’t found it documented anywhere, but tests clearly show it does at least for arrays). Spring.NET, in exactly reversed fashion, has it documented, but broken in the release I have tested (see link in the table, howevers, seems to be fixed in the 1.2.0).

All other frameworks do not support list injection out of the box. It is probably implementable in most of them, so it is not such a big deal. However, I have tried to build an ArraySubDependencyResolver for Castle, and while trivial implementation works, correctly supporting circular references is a major pain I haven’t solved yet.

Unregistered resolution is also something I have talked about, as a solution to container pollution (nice rhyme!). It was a nice surprise to see that most of the frameworks except Castle and, it seems, Spring.Net have this feature. In Autofac you have to allow specific types to be resolvable, however it seems to work pretty well.

6. Final overview

The last section of this comparison will be the most subjective. The first thing I want to include is auto-mocking. Auto-mocking as a problem is well described in this post. In a nutshell, it is an ability to leverage IoC framework to automatically mock dependencies of the tested component.

IoC frameworks often position themselves as tools to simplify mocking, so I think it is important to have this simplification not only on the architectural level, but on the tool level as well. Of course, different people like different mock frameworks, however having an example integration with any mock framework really helps to implement your own.

In addition to auto-mocking, I added to the final overview some interesting features that are present in a single framework, and issues I encountered while working with specific frameworks.

As we can see, Autofac and StructureMap have an official (semi-official, for Autofac) mock framework integration example. Castle and Unity do not, however I was able to easily find appropriate samples. For Ninject and Spring.Net the containers are probably implementable, but I had no time to research it in detail.

Special features are very subjective, but two interesting things I noticed are contextual bindings in Ninject and container hierarchies in Autofac. Container hierarchies, in particular, seem to provide a solution for argument coupling. I will not describe these features in detail, however you may find it interesting to read about them.

Now special problems and pain points. In my opinion, Castle relies too much on “this is in trunk”. For example, Ayende has suggested me to use ResolveAll method to solve the generics problem I had with ResolveServices. However, there is no ResolveAll in RC3, as there is no fluent interfaces and other things. The problem with that is that I can not rely on specific version when discussing Castle with anybody, and I can not rely on having a tested and finalized version if I want to have new features.

I have not used Unity and Autofac in real work, but in tests I haven’t encountered any significant problems aside from unsupported features I already discussed.

In Ninject, there is a build problem under .Net 3.5 — sometimes I had to remove Ninject library from references and then re-add it, otherwise there was a build error about ExtensionAttribute defined twice. It was really annoying so I hope it will be fixed asap (or may be it already is).

With StructureMap, the most interesting discovery was that it is not limited to static classes. Until I was pointed to this fact, testing was quite painful, but now I can say I do not see any issues. Even better, it seems the this framework also provides an easy way to pass additional arguments by type, which is an easiest solution for argument-uncoupled factories.

Most problems with Spring.Net are based on the configuration complexity. The framework itself is powerful, but it would be nice to have autowiring and fluent configuration interface more visible in the documentation.

7. Conclusion

Judging from the comparison, I think my preferences for a new project lie between Autofac and Castle. Both frameworks understand the importance of good circular dependency resolution, and have a nice set of features. Right now I am considering Autofac, since I had a bad experience with Castle internals and hierarchical containers seem very promising. However Castle is much more mature framework, and Autofac probably has its downsides I just do not yet know about.

Ninject is probably also a good idea, I like its explicit modular system. However not being able to reliable build referencing projects on .Net 3.5 and no recursion handling are critical issues for me.

Unity is always a solid choice, it is well documented and well supported by Microsoft team. I disagree with its constructor choices and mandatory properties, but this may be less important for other people. No recursion handling here as well.

StructureMap is very interesting, since it has several surprising features that I was not expecting from any framework. For example, support for array resolution is a really neat step forward. The only important downsides of StructureMap I see now are recursion handling and constructor selection.

Spring.NET is probably the hardest choice with the most complex learning curve. It is powerful and extensible, but following the documentation it is very easy to end up with a large pile of hard-coded XML.

8. Tests and spreadsheets

To fill the gaps in the tables, I had to write some feature tests for the discussed frameworks.
MbUnit’s TypeFixture was invaluable for this purpose, since it allowed me to write each test exactly once.

You can get the test project and the complete spreadsheet here:

Code: Google Code
Spreadsheet: Zoho

The test project may also be useful to learn APIs of the reviewed frameworks.

kick it on

/2008/09/08/comparing-net-di-ioc-frameworks-part-2/feed/ 101
Google Chrome — short thoughts /2008/09/02/google-chrome-short-thoughts/ /2008/09/02/google-chrome-short-thoughts/#comments Tue, 02 Sep 2008 20:25:53 +0000 /index.php/2008/09/02/google-chrome-short-thoughts/

This may belong to Twitter.

  1. Comic — nicely done, but how about some good words for work of other people (especially when influenced by it)?


    • Process per tab
    • Private mode
    • Emphasis of domain name in the address bar


    • Search shortcuts
    • Speed dial (but Google improved it)
    • Tabs on top of address bar (which is the sane way to do it)


    • Search/address bar 
    • JavaScript JIT (Firefox 3.1)

    I understand why Microsoft is not saying “just like Firefox”, however a lot of Google PR is about being friendly.

    Also, I was a bit surprised by the multiple references to tab crashes not crashing the application. Does somebody really encounter often tab crashes? Opera crashes in extremely rare cases, and it is possible to restore a session anyway.

  2. Future? I still use Google Talk (and converted a lot of people), but it seems no one at Google cares anymore. There are at least three different Google Talk editions, with unrelated features (Labs Edition supports text conferencing, but not file sending).

    So it is interesting if Google is going to support Chrome if it does not get significant popularity. It is open-source, however, so my concerns may not prove important.

  3. Process explorer is a fabulous feature, a must have for every other browser. However, aside from that, a good JavaScript debugger is important.

  4. Chrome is a bit of a performance hog, not sure how it compares to Firefox, but it is definitely heavier than Opera, especially CPU-wise.

  5. Not exactly Chrome-related, but why people seem to want a relational database in the browser (Gears, HTML5)? JavaScript structures are already extremely usable and easy to work with. They also abstract fast searching (hashes). Why not use a JSON storage stored in a browser-specific way, instead of putting SQL specification into HTML?

In general, Chrome may replace Opera for me, depends on how well it works with saved passwords (Firefox is way worse than Opera’s Wand), gestures and its overall performance.

/2008/09/02/google-chrome-short-thoughts/feed/ 4
Google Reader and Zoho Spreadsheets /2008/08/20/google-reader-and-zoho-spreadsheets/ /2008/08/20/google-reader-and-zoho-spreadsheets/#comments Wed, 20 Aug 2008 19:20:48 +0000 /index.php/2008/08/20/google-reader-and-zoho-spreadsheets/ As far as I see, Google Reader does not show tables in my previous post (Comparing .NET DI (IoC) Frameworks, Part 1). Actually, it just skips the tables, making post somewhat cryptic.

I’ll try to fix it for part 2. In the meanwhile, reading the post through the blog interface may make more sense.

The tables are actually parts of an iframe-embedded Zoho spreadsheet, so it makes sense (though is very inconvenient).

/2008/08/20/google-reader-and-zoho-spreadsheets/feed/ 3