Who are we?

We are a group of enthousiastic people working for Nedap, a Dutch company with a single goal: solve problems.

Search
« 50,000 dutch nurses use NFC phones daily | Main | RVM installing the mysql gem ruby 1.9.1 under OSX »
Thursday
Aug042011

Faye integration into a rails app (+ testing!)

A few months ago we decided we wanted our website (Rails 3.1.rc5) to become more interactive. We wanted what the big guys had. We wanted to inform our users in real-time of stuff that was happening. Cool right?

Building the stuff was actually very easy. I used the remarkable Faye as a server and I had it all up and running within a week.

I decided on an architecture that would allow the real-time tech we used to be as unubstrusive as possible. Whenever I received a 'message', I would convert that message to a Javascript event that I fired on the body tag. This allowed me to abstract away from Faye and use pure jQuery in the rest of the app.

Whenever we wanted behaviour based on a message I could just bind a specific callback to that event from anywhere.

Using this architecture I started converting Caren (our app) to use as much real-time stuff as we could. It was awesome. Life was great.

But then I hit a wall. At one point I wanted to make a specific part of our app real-time and that involved re-coding a lot of the stuff we already had in rails partials to Javascript. Damn, duplication.

We couldn't remove the rails stuff because you still needed that for the old stuff that was already in the database. This was the first real obstacle we hit. Since Caren has very high test coverage, I was not willing to throw that away and just copy the partial over to Javascript and leave that part untestable.

I needed to solve two issues: First, I didn't want to code the stuff again. Second, I wanted it to be testable.

The first solution was relatively easy, although I am still contemplating if the nastyness is worth the lack of duplication. Time (or the comments) will tell.

I decided to create jQuery templates from my Rails partials. Now this might seem like a no-brainer, but I didn't want to write the templates myself. I wanted rails to generate them from my original partial. Enter the nastyness.

  jquery_template "personal_message_by_other", "/messages/message", locals

Now you might be thinking: this doesn't look so bad. Wait for it.

  def jquery_template id, template, locals={}
    tmpl = capture{ render(:partial => template, :locals => locals) }.gsub(/\n/,'')
    tmpl = ""
    tmpl = tmpl.gsub('http://$','$')
    tmpl = tmpl.gsub('%7B','{').gsub('%7D','}')    
    return tmpl.html_safe
  end

Again, not that bad. But getting there. Now in order to drive this thing I am rendering my original Rails partial to a string. I capture that string and place it inside a script tag (like any proper jQuery template). The magic happens in the locals.

The locals I pass normally are the ActiveRecord objects of my rails app. But for the jQuery template I use fake objects. That look like this:

  class Templates::Base

    attr_accessor :acts_as_new_record

    def initialize options={}
      if options[:acts_as_new_record].nil?
        self.acts_as_new_record = true
      else
        self.acts_as_new_record = options[:acts_as_new_record]
      end
    end

    def to_key
      ["${id}"]
    end

    def id
      "${id}"
    end

    def to_s
      self.id
    end

    def new_record?
      self.acts_as_new_record
    end  

    def persisted?
      !self.acts_as_new_record
    end

  end

They mimic an ActiveRecord object but instead of the database values, they return stuff like "${id}". This is what jQuery uses to replace values. This works like a charm. There are some snags: everything the rails partial echo's into the html must be a method on both the real and fake ActiveRecord model. This is especially interesting if you are using localization.

I use these templates together with the content of the message I get from faye. Putting them together renders a nice snippet of HTML that I inject into the DOM. Life is good again.

Now off to the second part. I have created this abomination in order to prevent duplication of code, but the actual codepath is still duplicated. Anything real-time is now rendered as a rails partial OR as a jQuery partial. And I am only testing the rails partials, since... well Javascript integration testing sucks.

But I wanted it tested. So I decided to bite the bullet. Caren uses Cucumber with Akephalos for integration testing. My first test was a horrible failure. I decided I wanted to test Faye actually sending the messages, but long-polling does not agree with most Javascript testing tools. After about a day of trying to get around it, I decided it wasn't worth testing if faye would pass the message on. I was more interested in the reaction to that message.

At the end, my stories looked like this:

  Scenario: Changing notes in real-time
    Given I am on Andre's page
    When Andre's "note" is updated to "Test notitie!" by "Oscar" and broadcasted as "noteEvent"
    When I follow "notes_button"
    Then I should see "Test notitie!"

And the core step in this story is implemented like this:

  When /^([^"]+)'s "([^"]+)" is updated to "([^"]*)" by "([^"]*)" and broadcasted as "([^"]*)"$/ do |person, field, value, updater, event_name|
    prev = Person.current 
    Person.current = Person.where( :first_name => updater ).first
    Person.where( :first_name => person ).first.update_attribute(field.to_sym, value)
    message = Activities::Activity.last.push[:data]
    page.driver.execute_script("page.pushClient.triggerEvent('#{event_name}',#{message.to_json})")
    Person.current = prev
  end

As you can see I manually trigger the event with the content (json) that my Rails app generated for me. This is as close to a real situation I could get. And this worked! Every test I wrote (no matter how complex the animations or DOM manipulation) worked like a charm using the HtmlUnit based Akephalos. Unfortunately when running the stories in a row, it all went to hell.

Meet the bane of my existence:

  Mysql2::Error: This connection is still waiting for a result, try again once you have the result

Huh? What? What does that even mean? People here agree: https://github.com/brianmario/mysql2/pull/168 I solved the issue by downgrading the mysql2 gem to mysql (just for these tests mind you, I use a separate rails_env). But that's not all. Since Akephalos uses a remote server to run the tests that need to share the same Mysql connection pool, more freakyness occurs. How about this one:

  Undefined method collect on NilClass

This one kept coming back, at random intervals. Sometimes it did, sometimes it didn't. This exception was raised on the following code:

  CareProvider.first

Yeah, that's freaky right? This was pure ActiveRecord code. After some investigation I found the culprit: ActiveRecord QueryCache. Apparently it did not play well with the tests. After I disabled that, it was smooth sailing.

  if Rails.env.javascript_test?  
    module ActiveRecord
      module ConnectionAdapters
        module QueryCache
          private
            def cache_sql(sql,binds)
              yield
            end
        end
      end
    end
  end

So now I have a fully tested RealTime web app. What do you think, worth it?

PrintView Printer Friendly Version

EmailEmail Article to Friend

References (31)

References allow you to track sources for this article, as well as articles that were written in response to this article.
  • Response
    Response: ning.com
    Moves on Rails - Journal - Faye integration into a rails app (+ testing!)
  • Response
    Response: Get the facts
    Moves on Rails - Journal - Faye integration into a rails app (+ testing!)
  • Response
    Moves on Rails - Journal - Faye integration into a rails app (+ testing!)
  • Response
    Football is truly 1 of the biggest sports in America. It has a major following.
  • Response
    Response: sports betting
    Moves on Rails - Journal - Faye integration into a rails app (+ testing!)
  • Response
    Response: TPE管
    2013年,纳川股份实现营业收入6.55亿元,同比增长65.2%,实现归属母公司净利润0,TPR管.92亿 TPR管 元,同比下滑5.4%。但其中,其传统管材销售仅增加0.5亿元,同比微增13%。 纳川股份上述董秘办公室工作人员对《第一财经日报》 塑胶管 记者表示,公司业务较多集中在市政管网工程领域,但地下排水管网改造受到市政前期规划等影响,塑胶管,要推动改造并非一日之功。"一些老城区在管网改造方面面临拆迁、影响交通等各方面的问题。此外,在政府资金层面,受到经济放缓的影响,也存在压力。" 相较 EVA管 中国
  • Response
    Actually this program can be quite sophsiticated and also valuable a single. The item has to be operated with some other server from the web program per se. Rather then accomplishing the idea, this rackup config report should be combined with the fundamental folder of the web page. The actual baove ...
  • Response
    Response: amazon.com
    The ability to update and maintain web applications without distributing and installing software on potentially thousands of client computers is a key reason for their popularity, as is the inherent support for cross-platform compatibility.
  • Response
    Moves on Rails - Journal - Faye integration into a rails app (+ testing!)
  • Response
    Response: groupwise inc
    Moves on Rails - Journal - Faye integration into a rails app (+ testing!)
  • Response
    Response: groupwise inc
    Moves on Rails - Journal - Faye integration into a rails app (+ testing!)
  • Response
    Response: groupwise inc
    Moves on Rails - Journal - Faye integration into a rails app (+ testing!)
  • Response
    Response: groupwise inc
    Moves on Rails - Journal - Faye integration into a rails app (+ testing!)
  • Response
    Moves on Rails - Journal - Faye integration into a rails app (+ testing!)
  • Response
    Response: Dr. Patel DDS
    Moves on Rails - Journal - Faye integration into a rails app (+ testing!)
  • Response
    Response: 淘宝网品
    中国证券网讯(记者 严洲)记者10月17日从 网上买家 农业部获悉,农业部开展了2014 板式家具 年中国最美休闲乡村和中国美丽田园推介活动,网上买家。经过地方推荐、专家评审和网上公示等程序,板式家具,认定北京市密云县干峪沟村等100个村为2014年中国最美休闲乡村,北京市密云县蔡家洼玫瑰花景观等140项农事景观为2014年中国美丽田园。   根据名单显示,中国最美休闲乡村分为特色民居村(29个)、特色民俗村(22个)、现代新村(28个)、历史古村(21个)等4大类。   中国美丽田园则分为,油菜花
  • Response
    Response: OneTwoTrade
    Moves on Rails - Journal - Faye integration into a rails app (+ testing!)
  • Response
    Response: Rashmi Patel
    Moves on Rails - Journal - Faye integration into a rails app (+ testing!)
  • Response
    Response: Dr. Rashmi Patel
    Moves on Rails - Journal - Faye integration into a rails app (+ testing!)
  • Response
    Moves on Rails - Journal - Faye integration into a rails app (+ testing!)
  • Response
    Moves on Rails - Journal - Faye integration into a rails app (+ testing!)
  • Response
    Response: OneTwoTrade
    Moves on Rails - Journal - Faye integration into a rails app (+ testing!)
  • Response
    Response: freedom mentor
    Moves on Rails - Journal - Faye integration into a rails app (+ testing!)
  • Response
    Moves on Rails - Journal - Faye integration into a rails app (+ testing!)
  • Response
    Response: OneTwoTrade
    Moves on Rails - Journal - Faye integration into a rails app (+ testing!)
  • Response
    Moves on Rails - Journal - Faye integration into a rails app (+ testing!)
  • Response
    Moves on Rails - Journal - Faye integration into a rails app (+ testing!)
  • Response
    Moves on Rails - Journal - Faye integration into a rails app (+ testing!)
  • Response
    October 22,Moncler Mens Jackets, 2013 | By William Freeman jordan-spizike-id Moncler Mens Jackets -og-print-01-570x427 jordan-spizike-id-og-print-02-570x376 jordan-spizike-id-og-print-03-570x376 jordan-spizike-id-og-print-04-570x376 The Jordan S
  • Response
    Response: 治安岗亭
       在东丽区津塘路与跃进路附近,有一座学雷锋志愿服务岗亭,可提供的服务项目多达十余项。这里不仅免费为行人提供打气筒、急救 保安岗亭 小药箱,还能为环卫工人热饭。只不过,服务项目不少,用的人却不多。   岗亭  昨天10:00左右,记者找到了这座志愿服务岗亭,这里看上去比报刊亭面积略大,一身红色装扮立在路口十分醒目,从外观上的宣传语不难看出, 这是一处学雷锋志愿服务基地。岗亭透明的大玻璃窗上还注明了服务项目,包括护送小学生过马路、设立失物招领专柜、为行人义务指路、提供急救小药箱,以及为 环卫工人免
  • Response
    Response: Moncler Outlet
    For the past few seasons, Frida Giannini, Creati Moncler Womens Vests ve Director of Gucci, has b Moncler Womens Jackets rought the house back to the forefront of our minds. With a steely yet sexy combination, Gucci Fall 2013 focuses on constructed silhouettes with femme fatale touches on every

Reader Comments (6)

to walk near the water UGG Knightsbridge Boots 5119 or netted shoes.
Back in the days, beach shoes were looked down Adidas Campus 80 upon as shoes

such as Camelot dvd setThe Little Mermaid on dvd and so on,1000s of dvds watting for you!!
Just wanna to say hi! Really, nice posts. By the way, Christmas is coming! and our store gave lots of free gifts for you.
christmas special: Free Shipping for all!!!

November 19, 2011 | Unregistered Commenterdvds

Consider a service provider that will assure the following excellent professionals who log in guarantee that every last nickel you only pay for just a cheap lv monogram empreinte handbag makes it worth while.In a word, amazing low priced lv leather quality handbags are truly wonderful substitutions involving expensive unique people.You may be pleased when using the superb info and excellence of best lv damier wallets.With all the swift continuing development of World-wide-web, several shops involving replica monogram vernis focus the webs current market.

December 6, 2011 | Unregistered CommenterDamier Azur Canvas Luggage

reatrey I will keep your new article. I really enjoyed reading this post, thanks for sharing.

December 8, 2011 | Unregistered CommenterMicrosoft Office 2010

<p>Mu Chen was silent, he admitted, Gagne says these are the facts, as he did now, just practice two" dragon sword" willugg boots sale feel extremely tired, wait until after the really on the batugg boots clearancetlefield, in a powerful army, it sure is hard to survive.</p>

December 9, 2011 | Unregistered Commenterwendy

Your article is nice, I read your article to learn a lot and hope to see your next article,air king replica look forward to your masterpiece, you can also see our ball gowns information,louis vuitton damier leather I hope it can give you You some convenient.

January 11, 2012 | Unregistered Commenterpeterjones

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>