Archive for the ‘Random’ Category

Kabisa at #arrrrcamp 2011

Friday, October 7th, 2011

Hi guys,

We’re posting this one right at ya from Arrrrcamp 2011 in Ghent. Arrrrcamp is one of the best known RoR events in Belgium. An event Kabisa is a proud sponsor of. Our crew is sitting in the sessions and we’ll report in this post after each event.

John W Long – Design workflow for Rails

We started the day with John W Long’s session about “Design workflows for Rails”. He states that within companies there needs to be knowledge about business, design and development and that the ideal situation is having knowledge of all three aspects. The more these aspects are in synergy with eachother the better. He talked about the things that designing will improve (easier programming, better customer understanding, etc)

Similar to positive aspects that iterative development brings along, the designing of application’s would need to be iterative, too. An agile workflow that would look as following was presented:

  • Create userstories
  • Design / Prototyping
  • Building
  • Verifying
  • Back to the 1st step, building new features

Conclusion: Integrating designing / prototyping in the iterative daily process will make things easier and will take communication with your customers to a higher level.

Elise Huard – Data Driven Development

Elise talked about DDD (Date Driven Development).
What we take home from her presentation:

  • Your first hunch about how your web application is going to work out (target wise) is wrong most of the time; you need to measure interaction and your assumptions and react to that.
  • It’s good to build interactive elements (ie:buttons on key locations) that will give you user feedback and that verify if your assumption is correct.
  • Check if there is interest in your product(idea) by creating a minimal viable product that you present to the world. Create an update subscription along with that and if there IS interest, you’ll have data (subscriptions) that prove it.

 

Jonas Nicklass – Practical testing for assorted languages

For Ruby it isn’t acceptable to write code and NOT test it. Jonas asking the croud who works using RoR and who tests their code: all hands go up.

Asking this for Javascript only few hands are raised.Javascript is something that has always been neglected regarding to testing.
Jonas talked about some design setups for testing Javascript; about unittesting javascript.You have to ask yourself what the goal for writing tests is. Jonas indicates he tests because it makes refactoring easier (remember the red-green-refectoring loop from TDD?). Because unittests are based on testing UNITS (it’s in the name) Jonas states that we need more units in JS; we don’t have the classes and modules that are present in Ruby.JQuery is often seen as a „way of building apps”, but it really is NOT.It doesn’t provided the structure needed for creating the units.
Jonas showed some possible solutions for providing structure to JS, giving some pros and cons along with all the options (think Mootools, CoffeeScript, Backbone, etc).

Unittesting JS
Jonas recommends using Jasmine („the best JS testing framework out there”) for unittesting in JS. It is the only stable solution out there at the moment.It is small but full featured.

When it comes to building JS using a packaged Jasmine solution Jonas build a rails gem (Evergreen) that makes integrating a breeze.This can be used for testing JS. It has nice syntax and can use CoffeeScript if you’d like. Add evergreen to your Gemfile, run the server and you’re good to go!
Running tests can be done in multiple ways:

  • In browsers (/evergreen)
  • Terminal (using a capybara driver); you can use Selenium, Webkit or ENVJS.

Jonas gave a demo using both execution methods. Evergreen showed all the tests present for JS. Clicking on one of them (or the „all” option) runs the test(s) and shows the outcome. Terminal execution was demoed as well and integrated nicely with Selenium.

Suggestions for writing JS unittests
Structure your JS code into functions and classesMostly test event bindingsWrite custom matchers like in RspecIsolate AJAX into seperate components (don’t test or use mocks) to avoid shaky testsTreate templates as fixtures (update them manually) by isolating JS as much as possible from the Ruby code. This depends on JS being setup Simplify your DOM & Templates
Integration testing JS codeJonas talked about Capybare to simulate JS interaction performed by the user and demoed it using Cucumber.

At Kabisa we already use this. Check it out at the web for more info:https://github.com/jnicklas/capybara

Finally, Jonas gave some tips for a better JS integrations test setup:

  • avoid asserting on urls, cookies, session, application state
  • Asserting to what you can see (interfacing) makes the test more stable. It avoids one for having to update the integration tests constantly.
  • Be as lenient as possible (avoid specific selectors)

 

Roy Tomeij – Stop Swashbucklin’ and Shipshape yer Front-end

Roy talked about Frontend meta languages and The Rails 3.1 asset pipeline.The target audience were people with little Haml experience.
Roy’s statement: compilers don’t create bad code, coders do (regarding Haml output HTML)
A few motivations for using front-end meta language:

  • Quick results
  • DRY (!)
  • Unforgiving compilers, so less errors
  • Better performance „out of the box”
  • Maintainable code

The front-end meta languages Roy talked about were Haml, Sass(&Compass) & CoffeeScript. He showed examples alongside the output that these language generate (it being HTML, CSS or Javascript).

Roy finalized his talk about Rails 3.1 asset pipelines and where to put all the various assets.

In conclusion: frontend meta languages keep your code clean and maintainable so it won’t hurt to take them into serious consideration!

 

Andrew Nesbitt – A/B Testing everything with Split

Andrew talked about the question: are you making the best out of your pages?
A/B testing is the process of sharing two designs with your users and learning what works best.
Andrew also showed an example of a salespitch text that – by simply showing another title – made +30% sales.

Some existing  A/B testing frameworks out there:

  • Abingo
  • niceSevenMinuteAbs
  • Vanity

Because these frameworks didn’t go all the way, Andrew wrote the Split library.

Some approaches that could be used for A/B testing:

  • Swap css stylesheets
  • Render different templates / partials
  • Set different variables at the controller level
  • Hack the page with JS

Split depends on two classes: Split::Experiment and Split::Alternative. These two, accompanied with a CRUD interface make up Split.

Andrew showed an example using an ab_test statement that showed one of two button images (50% chance you see either option a or b).
In the controller’s signup function a „finished” statement was added, after which you can check in the CRUD interface what option is most chosen / successful.The example showed how simple but powerfull this works.

A new feature of Split is weighted averages; you can now define ratio’s in which to serve option A or B.
Another nice feature of Split is the option to ignore certain IP adresses. This helps preventing your stats being mangled by colleagues who develop or test around.

Some tips regarding to A/B Testing

  • If you would want to get definitive favored choices from your users, test BIG changes. The bigger the change the more you’ll see the preffered version.
  • A typical A/B test would be ~2 weeks. Mind that this has to be longer if you have a low volume website.
  • Don’t run 2 tests that cross eachother’s stream. This will make the results inconclusive.

Conclusion: having trouble discussing the best solution to put out there? Use A/B testing to give all the options a try and let the stats choose a winner.

 

Cory Haines – Fast Rails Tests

Cory started right on with showing the not-so-speedy process of running Rspec tests.Right after that Cory kicks off some tests that show all greens  „near-instantly”…
Cory talked about the fact that it’s crazy to see such a key element taking up so much time.Thinking back on where we were years ago, thinks aren’t all that bad (referring to the time were an optionally filled /test map resided in projects).Rails changed all that by introducing ways to implement unittesting and integration testing.
He described the evolution of coding; moving business related code to the models. Good for the code, not so good for the speed of tests.

The difference between the Test First and TDD approach is that with TDD,  after you feel the pain, the whole design of the code is corrected instead of the test itself. After that, Cory talked about what „good design” is. His definition of a good design is: a design that is easy to change. He referred to businesses and their need to adapt themselves to their surroundings.
The core pain with Rails testsuites is not „the databases”, even though FactoryGirl or in-memory db’s are trying to create faster experiences. The actual pain of slow tests is Rails itself. Rails is the biggest 3th party dependency in our apps.

Cory demonstrated a way to run rspec on a new directory (fast_spec/speedy_shop/calculates_total_price.rb) in which a ruby file, with only the business logic from the shopping cart model in it,  requires the original codefile followed by the Rspec tests. Using this implementation, spec_helper (that starts rails, and generates all this stuff for you) is not used so you don’t have to wait for the time that would normally take.

He states that one shouldn’t be testing Rails itself and one certainly should not test database interaction by using mocks, because the best test for functionality that interacts with the db IS interacting with the DB (2 db records, one for failing and one for success scenario).
Testing the regular Rspec stuff mostly includes testing the rails configuration that you have put up for your app. This needs to be tested, but not in your main developing circle: build speedy tests to test the business logic in a fast way, and make that process a lot faster (without the accompanying sough).

Conclusion: extracting your business functionality and getting them on track with speedy rspec tests will improve your day-to-day developing cycle and make it less intensive to change simple things and having to run heavy tests.

 

Share sessions between Rails 2 and Rails 3 applications

Wednesday, October 27th, 2010

This week we started building a Rails 3 application for one of our customers which had to share data with their existing Rails applications, which were built with version 2.1.2 and 2.3.8.

Although session configuration differs from version 2 to 3, getting this done wasn’t such a hard job, mainly thanks to this blogpost written by Dan McNevin.

Basically this means the sessions are configured as follows:

# Rails 2.1.2
# config/environment.rb
...
Rails::Initializer.run do |config|
  config.action_controller.session = {
    :session_key    => '_sso_session',
    :secret         => 'a really long hex string'
  }
  config.action_controller.session_store = :cookie_store
end
 
ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:session_domain] = '.rails.local'
# End of Rails 2.1.2
 
# Rails 2.3.8 (and probably 2.3.x)
# config/initializers/session_store.rb
ActionController::Base.session = {
  :domain => '.rails.local',
  :key => '_sso_session',
  :secret => 'the same really long hex string'
}
# End of Rails 2.3.8
 
# Rails 3.0.1
# config/initializers/session_store.rb
[AppName]::Application.config.session_store :cookie_store, {
 :key => '_sso_session',
 :domain => '.rails.local'
}
 
# config/initializers/secret_token.rb
[AppName]::Application.config.secret_token = 'the same really long hex string'
# End of Rails 3.0.1

Session sharing between 2.1.2 and 2.3.8 worked fine, however when swapping to the 3.0.1 application I got the error:

ActionDispatch::Session::SessionRestoreError (Session contains objects whose class definition isn’t available.
Remember to require the classes for all objects kept in the session.
(Original exception: uninitialized constant ActionController::Flash::FlashHash [NameError])
):

In other words (or actually, my own words): the session contains an object (ActionController::Flash::FlashHash) which is unfamiliar to Rails 3.

To solve this problem, I added the class:

# Rails 3.0.1
# config/initializers/session_store.rb
module ActionController
  module Flash
    class FlashHash < Hash
      def method_missing(m, *a, &b)
      end
    end
  end
end
# End of Rails 3.0.1

Now, the error didn’t show up anymore and so was the session… I was able to switch from Rails 2 to Rails 3, but now the session didn’t contain a single keys!?!?
Assuming there is a require_user method doing the authentication, I added a

# Rails 3.0.1
# app/controllers/application_controller.rb
  def require_user
    y request
  ...
# End of Rails 3.0.1

to this controller action (which is short for puts request.to_yaml) and I was surprised to find the keys, which were stored by the Rails 2 app., in the env object in it’s action_dispatch.request.unsigned_session_cookie key:

# Rails 3.0.1
  y request.env['action_dispatch.request.unsigned_session_cookie']
  # => ---
      serial: 0
      _csrf_token: nTHGmUfA0sKh1rDZWvt+1tLZmG3fCWlhf8pkiHGMU5I=
      last_quote_time_en_US: !timestamp
        at: "2010-10-27 14:52:15.736034 +02:00"
        "@marshal_with_utc_coercion": true
      session_id: ef4c356efc12113792ecccbde65bba7a
      user_id: 35
      lang: en_US
      shown_quotes_en_US:
      - 17688
      ...
# End of Rails 3.0.1

Pretty hopeless by now, I decided to get the keys I needed out of this Hash and add them to the Rails 3 session manually:

# Rails 3.0.1
  session[:user_id] = request.env['action_dispatch.request.unsigned_session_cookie']['user_id']
# End of Rails 3.0.1

But this surprised me even more, finding a fully populated session only after adding one key.

Time to investigate the actionpack gem.
When you add a key to the session object, this will call the []=-method in the ActionDispatch::Session::SessionHash class. The []=-method internally calls a private method load_for_write!. My thinking was (since diving deeper into the code didn’t come to my mind) that the Rails 3 session is fully populated, but not yet loaded when going or returning to the Rails 3 application.

This was an easy one, I just had to reload the session before using it:

# Rails 3.0.1
# app/controllers/application_controller.rb
  def require_user
    session.send(:load_for_write!)
  ...
# End of Rails 3.0.1

Problem solved? Well, not completely. I was able to successfully browse from the Rails 2 app. to Rails 3, without losing my session, but going back to the Rails 2 app. introduced another problem: keys initially stored as symbols were now turned into string because of the Rails 3 app.

Initially, I tried to solve this by converting all keys back into symbols, but this should introduce another problem, since I was not sure if all session keys were stored as symbols. Rails itself stores the Flash object in the session into the “flash” key, instead of :flash.

A better approach is to patch the CGI::Session object and make sure all keys can be stored and retrieved as both string and symbols:

# Rails 2.1.2 and 2.3.x
# config/initializers/load_patches.rb
#
# Loads patches stored in lib/patches.
Dir[RAILS_ROOT + "/lib/patches/**/*.rb"].each { |file| require file }
 
# lib/patches/cgi/session.rb
require 'cgi/session'
 
# Patching CGI:Session so that it on longer matters if you retrieve a session
# value using a String, Symbol, ... This in order to make it play nicely
# together with Rails 3.
#
# = Examples
#
#  session[:foo] = "Bar"
#  session[:foo] # => "Bar"
#  session["foo"] # => "Bar"
#
#  session["qux"] = "Baz"
#  session[:qux] # => "Baz"
#  session["qux"] # => "Baz"
class CGI #:nodoc:
  class Session #:nodoc:
    def [](key)
      @data ||= @dbman.restore
      @data[key.to_s]
    end
 
    def []=(key, val)
      @write_lock ||= true
      @data ||= @dbman.restore
      @data[key.to_s] = val
    end
  end
end
 
# End of Rails 2.1.2 and 2.3.x

Now it worked! I am able to share sessions between Rails 2.1, 2.3.x and Rails 3.0 application.

To wrap things up:

# Rails 2.1.2
# config/environment.rb
...
Rails::Initializer.run do |config|
  config.action_controller.session = {
    :session_key    => '_sso_session',
    :secret         => 'a really long hex string'
  }
  config.action_controller.session_store = :cookie_store
end
 
ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:session_domain] = '.rails.local'
 
# config/initializers/load_patches.rb
Dir[RAILS_ROOT + "/lib/patches/**/*.rb"].each { |file| require file }
 
# lib/patches/cgi/session.rb
require 'cgi/session'
 
class CGI #:nodoc:
  class Session #:nodoc:
    def [](key)
      @data ||= @dbman.restore
      @data[key.to_s]
    end
 
    def []=(key, val)
      @write_lock ||= true
      @data ||= @dbman.restore
      @data[key.to_s] = val
    end
  end
end
# End of Rails 2.1.2
 
# Rails 2.3.8 (and probably 2.3.x)
# config/initializers/session_store.rb
ActionController::Base.session = {
  :domain => '.rails.local',
  :key => '_sso_session',
  :secret => 'the same really long hex string'
}
 
# config/initializers/load_patches.rb
Dir[RAILS_ROOT + "/lib/patches/**/*.rb"].each { |file| require file }
 
# lib/patches/cgi/session.rb
require 'cgi/session'
 
class CGI #:nodoc:
  class Session #:nodoc:
    def [](key)
      @data ||= @dbman.restore
      @data[key.to_s]
    end
 
    def []=(key, val)
      @write_lock ||= true
      @data ||= @dbman.restore
      @data[key.to_s] = val
    end
  end
end
# End of Rails 2.3.8
 
# Rails 3.0.1
# config/initializers/session_store.rb
[AppName]::Application.config.session_store :cookie_store, {
 :key => '_sso_session',
 :domain => '.rails.local'
}
 
module ActionController
  module Flash
    class FlashHash < Hash
      def method_missing(m, *a, &b); end
    end
  end
end
 
# config/initializers/secret_token.rb
[AppName]::Application.config.secret_token = 'the same really long hex string'
 
# app/controllers/application_controller.rb
  def require_user
    session.send(:load_for_write!)
  ...
# End of Rails 3.0.1

Next job is to upgrade the legacy code to Rails 3…