<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>validates_presence_of :purpose : Tag couchdb, everything about couchdb</title>
    <link>http://lenaherrmann.net</link>
    <language>en_US</language>
    <ttl>40</ttl>
    <description>Lena Herrmann</description>
    <item>
      <title>Thesis is done - thanks for your support!</title>
      <description>&lt;p&gt;I&amp;#8217;m done!! Three weeks ago &lt;a href="http://blog.couch.io/post/903223195/diplom-thesis-realisation-of-a-distributed-application"&gt;I handed in my diplom thesis&lt;/a&gt;. The practical part was the design and implementation of a distributed, replicatable &lt;a href="http://en.wikipedia.org/wiki/Outliner"&gt;outliner&lt;/a&gt; with the NoSQL database &lt;a href="http://couchdb.apache.org/"&gt;CouchDB&lt;/a&gt;. As theoretical work I evaluated Distributed Systems, CouchDB, various web technologies and programming strategies. I still have to pass the oral exam, but my degree is finally very close - soon I can call myself a graduated computer scientist. Yay!&lt;/p&gt;


&lt;p&gt;As soon as academic procedures allow it, I&amp;#8217;ll publish the text and the source code of the thesis here. Until then I&amp;#8217;d like to say thank you to a few people. As the foreword of thesis won&amp;#8217;t get as much attention as this blog, I&amp;#8217;ll do it here.&lt;/p&gt;


&lt;p&gt;First first first of all, my appreciation goes to my boys at home and my parents for backing me up by spending loads of time with my child. Not only my professional life is based upon their support. THANKS.&lt;/p&gt;


&lt;p&gt;Further thanks go to:&lt;/p&gt;


&lt;p&gt;&lt;a href="http://upstre.am/"&gt;Upstream&lt;/a&gt; for generously sponsoring a part of the time I spent researching - especially &lt;a href="http://www.twitter.com/langalex"&gt;Alex Lang&lt;/a&gt; for his support and coming up with the initial idea. Without them I probably would have done some usability study for the drawer, or I would have had lots of fun in some enterprisey Java company. Also I felt I wanted to strive hard to write good code in my thesis to match Upstream&amp;#8217;s high standards for code quality and test coverage - not a bad thing.&lt;/p&gt;


&lt;p&gt;Allround guru &lt;a href="http://jan.prima.de/"&gt;Jan Lehnardt&lt;/a&gt; - he not only knew and happily provided the answers to all questions I had about CouchDB, databases in general, JavaScript, LaTeX, typography and virtually everything else; he was also constantly encouraging and guiding me to contribute to open source projects, trusting in my skills and doing &lt;i&gt;awesome&lt;/i&gt; things. One day I&amp;#8217;ll also submit a talk to one of your conferences, word :-)&lt;/p&gt;


&lt;p style="font-size:0.9em;"&gt;&lt;img width="532"  src="/files/thesis_sofa.jpg" alt="Jan Lehnardt, myself and Alex Lang on the couch with thesis."/&gt;&lt;br/&gt;

Jan Lehnardt, myself and Alex Lang on the couch with thesis.&lt;/p&gt;


&lt;p&gt;My professor &lt;a href="http://prof.beuth-hochschule.de/edlich/"&gt;Stefan Edlich&lt;/a&gt; for doing a great job in mentoring me. He is also the university professor with the most phenomenal email response times I have ever heard of.&lt;/p&gt;


&lt;p&gt;&lt;a href="http://github.com/ur5"&gt;Urs&lt;/a&gt;, Andy and Ischa advised me on the structuring and helped to make the result look quite scientific. They patiently explained to me how to go about writing such a long academic text. Ischa also is one of the very few people who actually read the whole thing in one piece.&lt;/p&gt;


&lt;p&gt;Several lovely people who helped at certain points during the research or the programming, listened with interest and/or assisted figuring out specific problems: &lt;a href="http://freelancing-gods.com/"&gt;Pat Allan&lt;/a&gt; (getting started was the hardest part); Urs again (many small and big issues all along the way); &lt;a href="http://github.com/endor"&gt;Frank Pr&#246;&#223;dorf&lt;/a&gt; and &lt;a href="http://twitter.com/overbryd"&gt;Lukas Rieder&lt;/a&gt; (figuring out with me what a Couchapp actually is); &lt;a href="http://till.klampaeckel.de/"&gt;Till Klamp&#228;ckel&lt;/a&gt; (skilled advice about Lounge and EC2); &lt;a href="http://www.kristinaschneider.com/"&gt;Kristina Schneider&lt;/a&gt; (tricky CSS shit); &lt;a href="http://twitter.com/freaklikeme"&gt;Thilo Utke&lt;/a&gt; and &lt;a href="http://twitter.com/svenfuchs"&gt;Sven Fuchs&lt;/a&gt; (die HTMLUnit bug, die);  &lt;a href="http://twitter.com/i2w"&gt;Ian White&lt;/a&gt; (this one bug in the code you had no idea what it was for but figured it out anyway); and various people from the Ruby and Javascript communities who helped me to shape my ideas by asking basic or challenging questions.&lt;/p&gt;


&lt;p&gt;Some more people who wrote great open source programs, did great support and were always open to ideas: my thanks go to a good part of the CouchDB community here, and also to &lt;a href="http://quirkey.com/"&gt;Aaron Quint&lt;/a&gt; and &lt;a href="http://twitter.com/tjholowaychuk"&gt;TJ Holowaychuk&lt;/a&gt;.&lt;/p&gt;


&lt;p&gt;Finally my army of proofreaders! I split the text up in very small parts so that also people with a limited time budget had no excuse - I can really recommend this approach ;-). The list includes almost everyone mentioned above, and also: &lt;a href="http://twitter.com/adrianlang"&gt;Adrian&lt;/a&gt;, Anne, &lt;a href="https://www.xing.com/profile/Birgit_Behringer"&gt;Birgit&lt;/a&gt;, &lt;a href="http://twitter.com/bumi"&gt;Bumi&lt;/a&gt;, Diana, &lt;a href="http://ekedase.blogspot.com/"&gt;Eike&lt;/a&gt;, &lt;a href="http://twitter.com/flofff"&gt;Flo&lt;/a&gt;, Grushi, &lt;a href="https://www.xing.com/profile/Helge_NeuschwanderLutz"&gt;Helge&lt;/a&gt;, Jan S., Micha, Neels, Ole and &lt;a href="http://www.huesler-informatik.ch/"&gt;Patrick&lt;/a&gt;.&lt;/p&gt;


&lt;p&gt;The very last one goes to my sweetheart for brightening my life in those last stressful months.&lt;/p&gt;


&lt;p&gt;Thank you everyone! I&amp;#8217;m glad to know you all!!&lt;/p&gt;
</description>
      <pubDate>Thu, 05 Aug 2010 10:41:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:8b192edb-c0eb-431c-ba7e-112050446cc0</guid>
      <comments>http://lenaherrmann.net/2010/08/05/thesis-is-done-thanks-for-your-support#comments</comments>
      <category>thesis</category>
      <category>thoughts</category>
      <category>couchdb</category>
      <trackback:ping>http://lenaherrmann.net/trackbacks?article_id=17</trackback:ping>
      <link>http://lenaherrmann.net/2010/08/05/thesis-is-done-thanks-for-your-support</link>
    </item>
    <item>
      <title>Security in CouchDB: changing the authentication db</title>
      <description>&lt;p&gt;I recently &lt;a href="http://lenaherrmann.net/2010/04/28/writing-a-testsuite-for-the-couchdb-api"&gt;wrote a testsuite for CouchDB&amp;#8217;s Javascript HTTP API&lt;/a&gt;. Among the things I tested were the security methods. That&amp;#8217;s those that deal with authentication - like signup, login, logout and so on. The challenge was to make sure my tests wouldn&amp;#8217;t interfere with the CouchDB setup of the person running the tests.&lt;/p&gt;


&lt;h3 style="color:#949490;margin-left:6px;"&gt;Background&lt;/h3&gt;


&lt;p&gt;CouchDB stores the information about the database users in a special database, the authentication_db. The default authentication_db is called &amp;#8220;_users&amp;#8221;. So this is where your user data is saved when you sign up without taking care about the details, e.g. when you use &lt;a href="http://github.com/lenalena/couchdb/blob/testsuite/share/www/script/jquery.couch.js#L112-119"&gt;the signup method provided by the JQuery HTTP API&lt;/a&gt;. Read more about the ways to interact with the authentication_db in &lt;a href="http://github.com/lenalena/couchdb-http-api-docs/blob/master/couch_js_security_methods.markdown"&gt;the HTTP API documentation&lt;/a&gt; (&lt;a href="http://github.com/lenalena/couchdb-http-api-docs/blob/master/jquery_couch_js_security_methods.markdown"&gt;JQuery methods&lt;/a&gt;), and read more about general CouchDB security &lt;a href="http://wiki.apache.org/couchdb/Security_Features_Overview"&gt;in the CouchDB Wiki&lt;/a&gt; or in &lt;a href="http://books.couchdb.org/relax/reference/security"&gt;The Definitive Guide&lt;/a&gt;.&lt;/p&gt;


&lt;h3 style="color:#949490;margin-left:6px;"&gt;How to set the authentication_db&lt;/h3&gt;


&lt;p&gt;When you e.g. create database users from within your tests, how can you make sure they don&amp;#8217;t mess up the actual authentication_db, the one the unsuspecting user might use in production? Of course you have to create a custom authentication_db for storing the user information, and make CouchDB use that one. And after your tests are finished, you want CouchDB to use the original authentication_db again, so that only your testsuite uses the custom users database.&lt;/p&gt;  


&lt;p&gt;So there is one function you call right at the beginning of the part of the testsuite that&amp;#8217;s concerned with security features:

&lt;div class="CodeRay"&gt;&lt;pre&gt;&lt;span class="CodeRay"&gt;&lt;span class="kw"&gt;function&lt;/span&gt; useTestUserDb(){
  test_users_db = &lt;span class="kw"&gt;new&lt;/span&gt; CouchDB(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;test_users_db&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;);
  &lt;span class="kw"&gt;var&lt;/span&gt; xhr = CouchDB.request(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;PUT&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;/_config/couch_httpd_auth/authentication_db&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, {
    &lt;span class="ke"&gt;body&lt;/span&gt;: JSON.stringify(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;test_users_db&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)
  });
  &lt;span class="kw"&gt;if&lt;/span&gt;(&lt;span class="kw"&gt;typeof&lt;/span&gt;(old_auth_db) == &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;){
    old_auth_db = xhr.responseText.replace(&lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="ch"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;/span&gt;,&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;).replace(&lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="k"&gt;&amp;quot;&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="mod"&gt;g&lt;/span&gt;&lt;/span&gt;,&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;);
  }
}&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;


&lt;p&gt;We create a custom authentication_db (&amp;#8220;test_users_db&amp;#8221;) and write its name into the CouchDB configuration. If we don&amp;#8217;t have the name of the old authentication_db stored yet (the one that&amp;#8217;s called &amp;#8220;_users&amp;#8221; per default, but might be called differently), we strip this name from special characters and store it in a variable, so we can restore it later. This is the function we call at the very end of the testsuite:

&lt;div class="CodeRay"&gt;&lt;pre&gt;&lt;span class="CodeRay"&gt;&lt;span class="kw"&gt;function&lt;/span&gt; useOldUserDb(){
  CouchDB.request(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;PUT&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;/_config/couch_httpd_auth/authentication_db&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, {
    &lt;span class="ke"&gt;body&lt;/span&gt;: JSON.stringify(old_auth_db)
  });
  test_users_db.deleteDb();
}&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;


&lt;p&gt;In the last line, we delete our custom authentication_db. Everything is back to normal, there is no trace of our tests left. You can have a look at &lt;a href="http://github.com/lenalena/couchdb/blob/testsuite/share/www/spec/couch_js_class_methods_spec.js"&gt;how it looks like in production.&lt;/a&gt;&lt;/p&gt;


&lt;h3 style="color:#949490;margin-left:6px;"&gt;How to work with the authentication_db&lt;/h3&gt;


&lt;p&gt;You can create a new user like this:

&lt;div class="CodeRay"&gt;&lt;pre&gt;&lt;span class="CodeRay"&gt;&lt;span class="kw"&gt;var&lt;/span&gt; userDoc = CouchDB.prepareUserDoc({&lt;span class="ke"&gt;name&lt;/span&gt;: &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Gaius Baltar&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="ke"&gt;roles&lt;/span&gt;: [&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;president&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;]}, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;secretpassword&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;);
test_users_db.save(userDoc);&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;


&lt;p&gt;Then you are logged in and can do whatever you want. About working with users and roles, read the &lt;a href="http://books.couchdb.org/relax/reference/security"&gt;chapter in The Definitive Guide&lt;/a&gt;. When you&amp;#8217;re done, delete the userDoc from the authentication_db, as the authentication_db has to be empty before it can be deleted:
  
&lt;div class="CodeRay"&gt;&lt;pre&gt;&lt;span class="CodeRay"&gt;test_users_db.deleteDoc({&lt;span class="ke"&gt;_id&lt;/span&gt; : userDoc.id, &lt;span class="ke"&gt;_rev&lt;/span&gt; : userDoc.rev})&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
</description>
      <pubDate>Thu, 29 Apr 2010 10:40:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:f3260d78-d5a7-4750-9429-b21c2cef7f0a</guid>
      <comments>http://lenaherrmann.net/2010/04/29/security-in-couchdb-changing-the-authentication-db#comments</comments>
      <category>couchdb</category>
      <category>javascript</category>
      <category>authentication</category>
      <category>stubbing</category>
      <trackback:ping>http://lenaherrmann.net/trackbacks?article_id=14</trackback:ping>
      <link>http://lenaherrmann.net/2010/04/29/security-in-couchdb-changing-the-authentication-db</link>
    </item>
    <item>
      <title>Writing a testsuite for the CouchDB API</title>
      <description>&lt;p&gt;&lt;a href="http://couchdb.apache.org"&gt;CouchDB&lt;/a&gt; is a database that I quite like. In fact, I haven&amp;#8217;t done much else this winter, as the programming part of my thesis involved diving into many of CouchDB&amp;#8217;s features. I had just finished my little program and was desperately looking for an excuse not to start writing the thesis. Mercifully &lt;a href="http://twitter.com/janl"&gt;Jan&lt;/a&gt; from &lt;a href="http://couch.io"&gt;Couchio&lt;/a&gt; came along and asked if I wouldn&amp;#8217;t like to write a testsuite for CouchDB&amp;#8217;s Javascript HTTP API, and the documentation with it. I wanted!&lt;/p&gt;


&lt;h3 style="color:#949490;margin-left:6px;"&gt;What is this HTTP API?&lt;/h3&gt; 


&lt;p&gt;If you have worked with CouchDB from a Javascript program before (&lt;a href="http://www.quirkey.com/blog/2009/09/15/sammy-js-couchdb-and-the-new-web-architecture/"&gt;eg Sammy.js is a good companion for a simple web application&lt;/a&gt;), you have probably used the methods &lt;a href="http://github.com/apache/couchdb/blob/trunk/share/www/script/couch.js"&gt;a file named couch.js&lt;/a&gt; provides. With this interface you can easily access the most common CouchDB functionality without having to handle the XMLHttpRequest yourself. It allows you to create a &amp;#8220;database object&amp;#8221; and then perform things on this object, for example save or delete documents:

&lt;div class="CodeRay"&gt;&lt;pre&gt;&lt;span class="CodeRay"&gt;&lt;span class="kw"&gt;var&lt;/span&gt; db = &lt;span class="kw"&gt;new&lt;/span&gt; CouchDB(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;test_db&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;);
db.createDb();

&lt;span class="kw"&gt;var&lt;/span&gt; doc = {&lt;span class="ke"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;_id&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Starbuck&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="ke"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Kara Thrace&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;};
&lt;span class="kw"&gt;var&lt;/span&gt; saved_doc = db.save(doc);

&lt;span class="c"&gt;// do something&lt;/span&gt;
db.deleteDoc({&lt;span class="ke"&gt;_id&lt;/span&gt; : &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;123&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="ke"&gt;_rev&lt;/span&gt; : saved_doc.rev});&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;


&lt;p&gt;If you&amp;#8217;re using JQuery, you can use the JQuery interface in &lt;a href="http://github.com/apache/couchdb/blob/trunk/share/www/script/jquery.couch.js"&gt;jquery.couch.js&lt;/a&gt;. It covers basically the same things as couch.js, but it uses, you guessed it, JQuery. That means you can send your requests asynchronously and handle the response in the callback. Same example:&lt;/p&gt;


&lt;div class="CodeRay"&gt;&lt;pre&gt;&lt;span class="CodeRay"&gt;&lt;span class="kw"&gt;var&lt;/span&gt; db = &lt;span class="pd"&gt;$&lt;/span&gt;.couch.db(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;spec_db&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;);
db.create();

&lt;span class="kw"&gt;var&lt;/span&gt; doc = {&lt;span class="ke"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;_id&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Starbuck&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="ke"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Kara Thrace&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;};
db.saveDoc(doc, {
  &lt;span class="ke"&gt;success&lt;/span&gt;: &lt;span class="kw"&gt;function&lt;/span&gt;(response){
    &lt;span class="c"&gt;// do something&lt;/span&gt;
    db.removeDoc({&lt;span class="ke"&gt;_id&lt;/span&gt; : &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;123&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="ke"&gt;_rev&lt;/span&gt; : response.rev});
  }
});&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;


&lt;p&gt;The code looks a bit more complicated, but with this API you have the full power of &lt;a href="http://lenaherrmann.net/2010/02/15/refactoring-asynchronous-code"&gt;asynchronicity&lt;/a&gt; in your head, where spelling the word asynchronicity is the worst part. &lt;/p&gt;


&lt;h3 style="color:#949490;margin-left:6px;"&gt;Writing the testsuite&lt;/h3&gt;


&lt;p&gt;I was free to choose which testing framework I wanted to use. The tests in CouchDB use a kind of home grown testing framework, which does its job, but is not really the state of the art of Unit Testing. The API tests aren&amp;#8217;t supposed to run within the normal CouchDB testsuite anyway. That is the one you always run in Futon when you installed CouchDB. So it was possible to use something different &amp;#8230; like &lt;a href="http://jspec.info/"&gt;JSpec&lt;/a&gt;. (I have also &lt;a href="http://lenaherrmann.net/2010/01/04/jspec-javascript-unit-testing-how-it-should-be"&gt;blogged&lt;/a&gt; and &lt;a href="http://lenaherrmann.net/2010/03/22/jspec-slides-of-my-talk-at-berlin-js-usergroup"&gt;talked&lt;/a&gt; about it in the past.)&lt;/p&gt;


&lt;p&gt;How to run JSpec tests? If you are familiar with JSpec tests, you know you define your tests in Javascript files, reference those from a HTML file and open that in a browser. When you want your tests to perform calls to the database, this won&amp;#8217;t work - you get the error message &amp;#8220;Access to restricted URI denied&amp;#8221;. Shortly this is because AJAX only allows connections to the same resource it is called from. Therefore you have to run the tests from within the database. CouchDB has a folder &lt;a href="http://github.com/apache/couchdb/blob/trunk/share/www/"&gt;share/www&lt;/a&gt;, which gets linked to the &amp;#8220;/_utils/&amp;#8221; path of your local installation. In this folder you find Futon. That&amp;#8217;s where you have to put your tests and all the helper scripts. Then you can run the tests directly from the couch by browsing to &lt;a href="http://127.0.0.1:5984/_utils/spec/run.html"&gt;/_utils/spec/run.html&lt;/a&gt;.&lt;/p&gt;


&lt;p&gt;Writing the testsuite was pretty straightforward. Along the way I found some bugs in the code (no surprise as it was written untested). One of the problems I had to solve I&amp;#8217;ve explained in another blogpost: &lt;a href="http://lenaherrmann.net/2010/04/29/security-in-couchdb-changing-the-authentication-db"&gt;How to overwrite the authentication_db so the tests don&amp;#8217;t interfere with the CouchDB setup.&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;There&amp;#8217;s one thing I&amp;#8217;m not completely happy with: The tests do really need kind of long to run, up to 3 minutes, depends a lot on the browser and CPU load. The other CouchDB Javascript tests are a lot faster. I think the new ones are a bit more comprehensive. And I preferred a very clean setup to how it&amp;#8217;s done in the old tests: I have a database created before every single assertion and have it dropped afterwards. So you know exactly in which state your database is during the test. I guess that&amp;#8217;s worth the longer runtime.&lt;/p&gt;


&lt;p&gt;So. If you want to see a lot of JSpec code in action, and want to learn more about CouchDB, &lt;a href="http://github.com/lenalena/couchdb/tree/testsuite/share/www/spec/"&gt;have a look at the code&lt;/a&gt;. At the moment it&amp;#8217;s in my fork only, the community (someone - you?) has yet to integrate it into Futon. For all the details, read &lt;a href="http://mail-archives.apache.org/mod_mbox/couchdb-dev/201004.mbox/%3C4BB74086.4010302@zeromail.org%3E"&gt;my rundown and left TODOs&lt;/a&gt;.&lt;/p&gt;


&lt;h3 style="color:#949490;margin-left:6px;"&gt;Documentation&lt;/h3&gt; 


&lt;p&gt;I also wrote API Docs for all the methods. It &lt;a href="http://github.com/lenalena/couchdb-http-api-docs"&gt;can be found here&lt;/a&gt;. Later you will find it nicely formatted in the &lt;a href="http://wiki.apache.org/couchdb/Reference"&gt;CouchDB Wiki&lt;/a&gt; and &lt;a href="http://www.couch.io/docs"&gt;on the Couchio website&lt;/a&gt;.&lt;/p&gt;


&lt;p&gt;If you find anything is missing or you don&amp;#8217;t understand it, feel free to drop me a line. I&amp;#8217;m happy to clarify it as long as it&amp;#8217;s all freshly in my brain.&lt;/p&gt;


&lt;p style="font-size:0.9em;"&gt;PS - I consider example data in tests important, so I took all the data from the SciFi series &lt;a href="http://en.battlestarwiki.org/wiki/Portal:Battlestar_Galactica_%28RDM%29"&gt;Battlestar Galactica&lt;/a&gt;, currently on my playlist and I luv it. And it gave me an excuse to read in the Battlestar Wiki every now and then when I was looking for correct spelling :)&lt;/p&gt;
</description>
      <pubDate>Wed, 28 Apr 2010 12:07:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:e603b6cb-97da-442f-91a0-7f91f70fb52d</guid>
      <comments>http://lenaherrmann.net/2010/04/28/writing-a-testsuite-for-the-couchdb-api#comments</comments>
      <category>jquery</category>
      <category>jspec</category>
      <category>couchdb</category>
      <category>testing</category>
      <category>documentation</category>
      <trackback:ping>http://lenaherrmann.net/trackbacks?article_id=13</trackback:ping>
      <link>http://lenaherrmann.net/2010/04/28/writing-a-testsuite-for-the-couchdb-api</link>
    </item>
    <item>
      <title>Bulk deletion of documents in CouchDB</title>
      <description>&lt;p&gt;For the couchapp I'm writing, I recently wanted to do a bulk delete operation with CouchDB, but found very little about it on the web. I don't know if my approach is the best way to go, but well, it works - if you know a better way, I'm happy to learn about it.&lt;/p&gt;

&lt;p&gt;In the file &lt;a href="http://github.com/lenalena/couchdb/blob/trunk/share/www/script/jquery.couch.js"&gt;jquery.couch.js&lt;/a&gt; that comes with CouchDB there are a couple of convenience methods to help you dealing with the database, documents and views. There is a method for deletion too (removeDoc), but it only can handle one document at a time. In the style of bulkSave, I added a bulkRemove method:

&lt;div class="CodeRay"&gt;&lt;pre&gt;&lt;notextile&gt;&lt;span class="CodeRay"&gt;bulkRemove: &lt;span class="kw"&gt;function&lt;/span&gt;(docs, options){
  docs.docs = &lt;span class="pd"&gt;$&lt;/span&gt;.each(docs.docs, &lt;span class="kw"&gt;function&lt;/span&gt;(i, doc){doc._deleted = &lt;span class="kw"&gt;true&lt;/span&gt;});
  &lt;span class="pd"&gt;$&lt;/span&gt;.extend(options, {&lt;span class="ke"&gt;successStatus&lt;/span&gt;: &lt;span class="i"&gt;201&lt;/span&gt;});
  ajax({
      &lt;span class="ke"&gt;type&lt;/span&gt;: &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,
      &lt;span class="ke"&gt;contentType&lt;/span&gt;: &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,
      &lt;span class="ke"&gt;dataType&lt;/span&gt;: &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;json&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, 
      &lt;span class="ke"&gt;data&lt;/span&gt;: toJSON(docs),
      &lt;span class="ke"&gt;url&lt;/span&gt;: &lt;span class="lv"&gt;this&lt;/span&gt;.uri + &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;_bulk_docs&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; + encodeOptions(options)
    },
    options,
    &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;The documents could not be deleted&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  );
},&lt;/span&gt;&lt;/notextile&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;In here I add the attribute "_deleted = true" to every document I want to delete, and then I do a bulk update with the documents. That's it.&lt;/p&gt;

&lt;p&gt;This is one possible use case: In the delete action of my controller I first fetch an array with the post and its comments from the couch (how to do this see below), and then I call the bulkRemove action on that array.

&lt;div class="CodeRay"&gt;&lt;pre&gt;&lt;notextile&gt;&lt;span class="CodeRay"&gt;couchapp.design.view(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;post_with_comments&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, {
  &lt;span class="ke"&gt;startkey&lt;/span&gt;: [params[&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;]],
  &lt;span class="ke"&gt;endkey&lt;/span&gt;: [params[&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;], {}],
  &lt;span class="ke"&gt;success&lt;/span&gt;: &lt;span class="kw"&gt;function&lt;/span&gt;(json) {
    &lt;span class="kw"&gt;if&lt;/span&gt; (json.rows.length &amp;gt; &lt;span class="i"&gt;0&lt;/span&gt;) { 
      &lt;span class="kw"&gt;var&lt;/span&gt; post_and_comments = json.rows.map(&lt;span class="kw"&gt;function&lt;/span&gt;(row) {&lt;span class="kw"&gt;return&lt;/span&gt; row.value}); 
      couchapp.db.bulkRemove({&lt;span class="ke"&gt;docs&lt;/span&gt;: post_and_comments}, {
        &lt;span class="ke"&gt;success&lt;/span&gt;: &lt;span class="kw"&gt;function&lt;/span&gt;() {
          flash = {&lt;span class="ke"&gt;message&lt;/span&gt;: &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;Post deleted.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="ke"&gt;type&lt;/span&gt;: &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;notice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;}
          redirect(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;#/outlines&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, flash);
        },
        &lt;span class="ke"&gt;error&lt;/span&gt;: &lt;span class="kw"&gt;function&lt;/span&gt;(response_code, msg) {
          flash = {&lt;span class="ke"&gt;message&lt;/span&gt;: &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;Error deleting post: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt; + msg, &lt;span class="ke"&gt;type&lt;/span&gt;: &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;};
        }
      });
    }      
  }
});&lt;/span&gt;&lt;/notextile&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;Note that the "flash" object I'm using is my own Sammy.js implementation of a Rails-like flash to show a message after a redirect. Maybe I'll share that in another blog post.&lt;/p&gt;

&lt;p&gt;As a bonus, this is the view I'm using to retrieve a post and its comments in one request. I'm following the pattern Christopher Lenz is recommending in his article on &lt;a href="http://www.cmlenz.net/archives/2007/10/couchdb-joins"&gt;CouchDB Joins&lt;/a&gt;.

&lt;div class="CodeRay"&gt;&lt;pre&gt;&lt;notextile&gt;&lt;span class="CodeRay"&gt;&lt;span class="kw"&gt;function&lt;/span&gt;(doc) {
  &lt;span class="kw"&gt;if&lt;/span&gt; (doc.type == &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Post&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;) {
    emit([doc._id, &lt;span class="i"&gt;0&lt;/span&gt;], doc);
  } &lt;span class="kw"&gt;else&lt;/span&gt; &lt;span class="kw"&gt;if&lt;/span&gt; (doc.type == &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Comment&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;) {
    emit([doc.post_id, &lt;span class="i"&gt;1&lt;/span&gt;, Date.parse(doc.created_at)], doc);
  }
}&lt;/span&gt;&lt;/notextile&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;When you call this view with

&lt;div class="CodeRay"&gt;&lt;pre&gt;&lt;notextile&gt;&lt;span class="CodeRay"&gt;startkey: [params[&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;]],
&lt;span class="ke"&gt;endkey&lt;/span&gt;: [params[&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;], {}]&lt;/span&gt;&lt;/notextile&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;you get an array with the post as the first element, and then all the comments that have the post's ID as post_id.&lt;/p&gt;

&lt;p&gt;This can of course also be used for things other than deleting: to show all the comments for a post, I get the array's first element and extract the title and content. Then I remove it from the array with

&lt;div class="CodeRay"&gt;&lt;pre&gt;&lt;notextile&gt;&lt;span class="CodeRay"&gt;json.rows.splice(&lt;span class="i"&gt;0&lt;/span&gt;,&lt;span class="i"&gt;1&lt;/span&gt;);        &lt;/span&gt;&lt;/notextile&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;You now have an array with only the post's comments and you can display them somehow.&lt;/p&gt;

</description>
      <pubDate>Tue, 22 Dec 2009 17:37:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:150befc7-1e89-47d5-ae0c-47d803336e73</guid>
      <comments>http://lenaherrmann.net/2009/12/22/bulk-deletion-of-documents-in-couchdb#comments</comments>
      <category>couchdb</category>
      <category>javascript</category>
      <category>json</category>
      <trackback:ping>http://lenaherrmann.net/trackbacks?article_id=8</trackback:ping>
      <link>http://lenaherrmann.net/2009/12/22/bulk-deletion-of-documents-in-couchdb</link>
    </item>
    <item>
      <title>Couchapp / Culerity glitches, part 1</title>
      <description>&lt;p&gt;Do you know what's behind these buzzwords: &lt;a href="http://couchdb.apache.org"&gt;CouchDB&lt;/a&gt; / &lt;a href="http://github.com/couchapp/couchapp"&gt;Couchapp&lt;/a&gt; / &lt;a href="http://code.quirkey.com/sammy/"&gt;Sammy.js&lt;/a&gt;? Not really? Then please look at this article now: &lt;a href="http://www.quirkey.com/blog/2009/09/15/sammy-js-couchdb-and-the-new-web-architecture"&gt;Sammy.js, CouchDB, and the new web architecture&lt;/a&gt;. The title says it all.&lt;/p&gt;


&lt;p&gt;This setup happens to be the field of my diploma thesis, friendly sponsored and supported by &lt;a href="http://upstream-berlin.com"&gt;Upstream&lt;/a&gt;. During the next months I'll develop a super duper thing with these technologies, and then I'll write 80 scientific pages about it. Yees, I'm not that much looking forward to the second part! Until then, I'm planning to semi-regularly blog about my findings.&lt;/p&gt;


&lt;p&gt;I'm not allowed to share the code before I have the diploma in my hands. But, because my workmate &lt;a href="http://twitter.com/bionadeholunder"&gt;Frank Proessdorf&lt;/a&gt; is so jealous of what I do, he and I started working on an app with a similar setup on our Upstream Research Fridays, to try things out. &lt;a href="http://github.com/lenalena/jsdoodle"&gt;Here it is&lt;/a&gt;. Don't expect anything yet.&lt;/p&gt;


&lt;p&gt;Today I'm going to tell you about some details of my testing setup. My *cough* supervising tutor &lt;a href="http://twitter.com/langalex"&gt;Alexander Lang&lt;/a&gt; already wrote down &lt;a href="http://upstream-berlin.com/2009/10/25/testing-couchapps-with-cucumber-and-culerity"&gt;all there is to know yet&lt;/a&gt; about Test-Driven Development with Cucumber-Culerity-Celerity and Couchapps.&lt;/p&gt;


&lt;p&gt;But there are a few things that will make your life hard, when the almost perfect Rails testing world has spoiled you as much as me. One thing is that we have to deal with a lot of asynchrony. Sammy renders the page, loads the data from the couch, and whenever the data is there it gets rendered into the page. The best way to do this is via callbacks. I'm going to write another blog post about that soon.&lt;/p&gt;


&lt;p&gt;In the integration test the problem is that celerity doesn't behave as it claims it does:&lt;/p&gt;

&lt;div class="CodeRay"&gt;&lt;pre&gt;&lt;span class="CodeRay"&gt;&lt;span class="gv"&gt;$browser&lt;/span&gt;.wait&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;is &lt;em&gt;supposed&lt;/em&gt; to make the test wait until everything is rendered completely. But for whatever reason it doesn't. The result is that your test fails because it searches for your expressions in an unfinished page.&lt;/p&gt;

&lt;p&gt;Because of that, in culerity we need something like:&lt;/p&gt;

&lt;div class="CodeRay"&gt;&lt;pre&gt;&lt;span class="CodeRay"&gt;sleep &lt;span class="fl"&gt;0.4&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;after every follow, press and go-to-path step. When using culerity in a Rails environment, this usually is enough. Sammy behaves a bit more unpredictable - it sometimes takes more than a whole second until everything is loaded.&lt;/p&gt;

&lt;p&gt;Solution? We can either increase the sleep time a lot, but that's not very nice, the features need long enough to run as it is. Fortunately we can use another method that's build into celerity: wait_while.&lt;/p&gt;

&lt;p&gt;I created a div "spinner" somewhere in index.html:
&lt;div class="CodeRay"&gt;&lt;pre&gt;&lt;span class="CodeRay"&gt; &lt;span class="ta"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="an"&gt;id&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;spinner&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;You can also add an actual spinner gif here. Just take care to place the div outside of your sammy element selector ("#main" by default), because this div gets replaced all the time.&lt;/p&gt;

&lt;p&gt;We need to show that div before every route that gets run. In application.js:
&lt;div class="CodeRay"&gt;&lt;pre&gt;&lt;span class="CodeRay"&gt;before(&lt;span class="kw"&gt;function&lt;/span&gt;() {
  &lt;span class="pd"&gt;$&lt;/span&gt;(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;#spinner&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;).show();
});&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;And we need to hide it after your rendering is completed. I haven't found an ideal solution where to do that, it depends on the application you are writing. As a quick and dirty fix, you can add it as a last line in sammy's partial function, in sammy.js:&lt;/p&gt;

&lt;div class="CodeRay"&gt;&lt;pre&gt;&lt;span class="CodeRay"&gt;partial: &lt;span class="kw"&gt;function&lt;/span&gt;(path, data, callback) {
  &lt;span class="c"&gt;//...&lt;/span&gt;
  &lt;span class="pd"&gt;$&lt;/span&gt;(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;#spinner&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;).hide();
},&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I prefer to have more control about when I hide it, as my partial calls sometimes have nested callbacks. So I put it in application.js, resp. in my controllers, after each render call or in the partial callbacks. I admit this still sucks a bit.&lt;/p&gt;

&lt;p&gt;Finally, amend the cucumber step:&lt;/p&gt;

&lt;div class="CodeRay"&gt;&lt;pre&gt;&lt;span class="CodeRay"&gt;&lt;span class="co"&gt;When&lt;/span&gt; &lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="k"&gt;I wait for the AJAX call to finish&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;
  sleep &lt;span class="fl"&gt;0.2&lt;/span&gt;
  puts &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;Waiting for page to load ...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="r"&gt;if&lt;/span&gt; &lt;span class="gv"&gt;$browser&lt;/span&gt;.div(&lt;span class="sy"&gt;:id&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;spinner&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;).visible?
  &lt;span class="gv"&gt;$browser&lt;/span&gt;.wait_while { &lt;span class="gv"&gt;$browser&lt;/span&gt;.div(&lt;span class="sy"&gt;:id&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;spinner&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;).visible?} 
&lt;span class="r"&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The browser still needs to sleep a bit before it actually finds the spinner div. But with this step in my common_culerity_steps.rb, the features finally run smoothly.&lt;/p&gt;</description>
      <pubDate>Wed, 04 Nov 2009 10:07:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:3e495ea7-1021-404e-b3ba-18267d409146</guid>
      <comments>http://lenaherrmann.net/2009/11/04/couchapp-culerity-glitches-part-1#comments</comments>
      <category>cucumber</category>
      <category>culerity</category>
      <category>celerity</category>
      <category>couchapp</category>
      <category>couchdb</category>
      <category>sammy.js</category>
      <category>javascript</category>
      <category>bug</category>
      <category>testing</category>
      <trackback:ping>http://lenaherrmann.net/trackbacks?article_id=6</trackback:ping>
      <link>http://lenaherrmann.net/2009/11/04/couchapp-culerity-glitches-part-1</link>
    </item>
  </channel>
</rss>
