Blog
what did i learn today
Uncategorized agile coderetreat tdd
coderetreat with Corey Haines

After RubyAndRails 2010 there was a coderetreat, and i had read two blog posts about it, and got really interested. So when i learned that coderetreat was coming to Belgium, i subscribed. Coderetreat as a concept was conceived by Corey Haines and a few friends, as a single full-day retreat doing nothing but practicing code writing. Actually, practicing performing TDD. The reasoning behind it is quite simple: if musicians need to practice to get perfect, why don't we? Musicians practice small parts over and over again, to get it right. I played volleyball, and we also practiced each single movement, so that during a game you wouldn't have to think twice how to bend your knees, hold your hands, place your feet, approach the net ... But as programmers we are almost always producing. And of course, there is experience that makes up for that. But while i am an experienced programmer, i am not an experienced TDD'er. So i could use some practice :) A coderetreat starts at about 8, and is filled with 5-6 sessions of about 45 minutes during which you try to solve a well-known problem together with another programmer, as a pair. The pairing is essential. After 45 minutes you delete the code, discuss the chosen paths, options, viewpoints, switch pairs, and off you go again. The problem we try to solve is Conway's game of life. A relatively simple problem, but just too big to solve in 45 minutes. My main reason to go was that i still struggle doing full-on TDD, and i really want to do that. At work I have paired intensively for a few weeks and done TDD and that was really instructive. Yet, on my own, i too easily slip back to designing, thinking of the solution, coding and then writing tests. What I expected was to get some kind of magic insight, to completely 'get' it :) Let me explain that in more detail. The game of life, while it's rules are quite simple, has to be translated to tests, and then to code. This process is still somewhat gray to me. What the day did teach me is that the way you write the tests, can definitely drive the design. And actually, when starting with a new problem, i can also imagine you really need a few iterations starting to get the correct tests to correctly identify the problem. A teacher once told me that once you find a certain solution to a problem, you definitely have to find a second solution, then select the best of both. That would make sure you don't have the worst solution ;) I always take this approach in my designs, but almost never in my code. But actually, if the tests drive the design, i can imagine it is indeed really helpful to really try some different approaches. And doing lots of these exercises will learn you to make the right decisions quicker. The first two sessions were just getting accustomed to the problem. The problem is so different from what we solve everyday, that it is pretty hard to get your head round it. In between every session Corey asks what we tried, and then highlights some key-points to consider, mainly the SOLID principles and the 4 rules of simple design. That really helps you focus, change perspective and allows you to explore different paths. While in the second and third session, we were really close to actually solving it, or so it felt, the more we knew, the harder it got ;) Corey did an amazing job, guiding us through the day. He mostly let us do our work, and like a good therapist he sometimes just asked questions so we could find the answers ourselves. Corey also pointed us to a very good explanation on his blog on the difference between Test First and Test Driven development by JB Rainsberger. I found it very enlightening, and also very supportive. What i do now ressembles closest to Test First, and i want to get at the Test Driven approach. And i will :) The complete day was just an awesome experience. So many people in one room, all willing to learn and share and get better at what we do. It was sometimes very humbling. I believed i was good at what i did, but i was wrong. There is so much room for improvement. But it makes me want to learn and improve. What also was humbling is the time and energy and knowledge Corey shared. I really enjoyed all the conversations in between the sessions, meeting the people. I really enjoyed the pairing, while at times i could also find it very hard as well. I did not work equally well with everybody. That also is something i need to work on. There was a nice mix of languages: most c#, some java, some ruby. I saw that much of the principles we use in ruby, are now also easily used in c#. But i did not need to do c# a lot, because most people wanted to give ruby a try. And a lot of people were impressed. That was very nice ;) So i am really buzzed now. I want to do more. I want to learn more. I want to practice. I want to teach. Spread the word. I want to do this at work more. I want my code to be better. To conclude i want to repeat something Corey said: we have the best job in the world. Every time we type, we create something where there first was nothing. That is amazing. That is a gift. For a moment, do not think about the requirements, the documents, your team-members and your boss and your clients, but remember why we got here in the first place: because we really liked coding. Because when we were young we just coded for fun. And it was fun. It can be fun. So make it fun again. That is something i want to keep with me. Since i switched to ruby it comes more easily to me, but still ... Have a look at the pictures and try to spot me ;) (hint: i am wearing the yellow t-shirt). Here is another impression from the same day. I hope to see you next time ;)

More ...
Uncategorized ruby arrrrcamp ruby on rails
Arrrrcamp 29/10/2010

The fourth and most international of all arrrrcamp editions. Also the first paying edition. As i said in mypost about the previous edition, i have been to all editions, and it is awesome to see how it has grown. The schedule was amazing, with an amazing array of international speakers, of which for me especially Yehuda Katz i looked forward to. The biggest problem i had was that two of my collegues gave a talk at the same time, both of which i wanted to see. Choices choices choices :) Let me give a quick impression of the talks i did see:

Keynote by Carl Lerche:

To be frank, i had totally no idea who Carl was. But his talk was very nice, talking about the speed improvements Rails does, under the covers, unknown at least to me, to get your site visible as soon as possible. Also the speed improvements they will take in the next version 3.1 and 3.2 to improve this even further. In short, they look for ways to serve the HTML so that a browser can download the page and all the assets quicker. Very new to me, touched some very interesting subjects as automatic spriting, the cache manifest ... But in the end, there is one thing that Rails can't solve: latency. That is the core time it takes to reach a server and get back to the client. A mere ping. If you want to reduce latency, you either make sure your servers are really close to all clients, ... or ... you work in a different way, and let your client handle more stuff. In short: using a minimal HTML which is only downloaded once, using the cache manifest and lots of javascript: back to client side code? Hold that thought, we'll come back to that later.

Escaping the Sludgy Swamp of Slow Tests by Joseph Wilk

This is a problem we are getting into at work. We try to solve that by running our tests in parallel. Joseph explains the routes they had taken in their team.

  • First off: use a lot of EC2 instances to reduce time. They went from 4hours build time to 11minutes, cheering developers, but very angry boss :) EC2 is not cheap!
  • So then they introduced some kind of low-level caching, which will cache sequences of SQL commands and returned a cached result. This sounds very interesting as that is what you see most in tests: a lot of similar db-setup to actually test something small.
  • Thirdly a great idea was to divide your tests into different sets, and only run those that are most likely to fail. Those that always succeed you only run nightly, and the ones that sometimes fail you also seperate so you can more easily test those, check loggings, and hopefully fix them. I also remembered the suggestions to use capybara together with env.jsto be able to test your javascript pages quicker. Good tips, and i especially like the graphics (and humour) on the slides :D

Refactoring++ : Refactor Everything A.T.F.T by Alain Ravet

A talk by a collegue of mine (had to miss Elise's talk, bummmmer, but her talk is recorded so i hope to see that later). I know most of his refactoring tips, because we have paired extensively and i have learned a lot by doing that. Alain took a small piece of code from the internet, an example to solve the Towers of Hanoi: the code was small and compact and seemed to be ok. But then Alain was able to find and show a lot of improvements. That was great. Aside from the standard refactorings (from the book) Alain also stressed the scanability of the code. This is also a pet-pieve of mine. Code has to be formatted nicely and correctly. In teams it is preferred that classes follow a bit the same structure, e.g. inside rails it is easy to make agreements where to expect the filters, constructors, public methods, ... Also Alain stressed the point to know your tools and your language. Ruby is a very expressive and compact language: use that to your benefit! Know the tool you are using, whether it is Rubymine, Textmate or VIM. And practice. Do code kata's, download some code and refactor-refactor-refactor.

Making it an Anti-Pattern is Not Enough by Timothy Payton and Sebastian Roebke :

I saw these guys last time, and i was very impressed by them at that time. Probably because what they said struck a chord, and matched my own experiences. This time, i found them less persuasive. I think maybe because my expectations were too high, they re-iterated a lot they said last time (or so it felt), the title was not explained anywhere (or i missed that), and i guess they were also having troubles with the single microphone (being two speakers) that made them more uncomfortable. Still a very nice talk, and Xing seems to be a very nice place to work.

Lightning Talks

  • Friendly attributes: apparently, in mysql, adding a columns is a very expensive operation. So this guy looked for alternatives, and came up with friendly attributes. This could be a very nice tool for someone if you are forced to use MySql (e.g. by your hosting provider). Otherwise i would suggest to use Postgresql.
  • A postgres textsearch without any extra server: this seemed very interesting and promising, but also very non-existant at the moment. He was in the process of turning it into a gem. Wait and see.
  • Yehuda's 10 minutes about concurrency in MRI: inspired by Elise's talk about concurrency, Yehuda dug a bit deeper into MRI threads and possible advantages. Unfortunately none of the big servers (passenger or unicorn) make any use of that. Only thin --threaded would profit from that. This really was a lightning talk! A lot of information in a short span, very interesting!
  • @Defv's recipe for a good rails rumble: after his third Rails Rumble Jan gave us the recipe: 1) a knock-out idea but simple enough so any visitor/judge would get it, simple enough to implement in 48 hours, and 2) good design. With the next emphasis being: simple! simple! simple! Reminds me a bit of what 37signals always says.
  • Wicegrid: i have used this grid in a few big projects i did, and really liked it. Nice that Yuri showed it, hope some more people start using it.
  • Restart of BRUG: this was nice news too. I have seen that amsterdam.rb and rotterdam.rb exist, and was wondering why we don't have such a "working" thing in Belgium. But the guys from Openminds offered to revive it, they will support the first three sessions, and then other sponsors would need take over (volunteers from the public immediately jumped in). Meetings would take place in different cities, so i am very curious how this would work out. That would be very nice ;)

Closing Keynote by Yehuda Katz:

Yehuda recently switched jobs, and together with Carl Lerche they are now both actively working on a framework called Sproutcore. In short, Sproutcore (or SC) is a javascript MVC framework. So, MVC on the client. In the talk from Carl we learned that the big problem is latency, so one way to improve or remove that, is instead of developing your web-application in the Rails way, is to use a more client-centric way. Yehuda then showed us a few moving parts of Sproutcore, which he intends to extract in seperate libraries so you could use them more easily. They also stressed that Carl and Yehuda are aiming for a very tight integration with Rails in the future. Altough it is not entirely sure what or how that will be be. You would be using Rails to just serve json-data. Rails still has its very strong points to be used in the backend for something like that. His talk left me a bit baffled, to be honest. The big advantage for me of the Rails framework is that there is one language, one platform to develop in, to cover both server and client side. That is a big advantage. It is also a big advantage compared to using AIR or Silverlight on the client, because you remove the duplication of all the models on both sides (in Rails you only need them on server side). That said, i do believe the future lies in heavier clients, like gmail and twitter shows us. The bindings that Yehuda showed, reminded my very much of the bindings offered in WPF and Silverlight. Possible advantages of Silverlight: with Ironruby you have the option to run ruby in the client as well. But, as the video-section of arrrrcamp themselves show: Silverlight it is not ready for all platforms. It does not work on my ubuntu 10.10. I am thinking now would be a good time to revive the idea of a ruby vm on javascript. In no specific order:

  • Hotruby was very promising but seems dead. With the speed improvements in current javascript vm's it would seem awesome to get some ruby on javascript too.
  • rubyjs: compiles ruby to javascript to be run in the browser
  • Js.class could be an alternative approach: use the classes and paradigms of ruby in javascript.
  • red: writes like ruby and runs like javascript
  • coffeescript: not quite ruby, that compiles into javascript I will dig into these alternatives in a later post :) The biggest advantage of sproutcore over extjs, AIR or Silverlight is that sproutcore is truly open source. Exciting times ahead :)

To conclude

As usual I really enjoyed arrrrcamp, the organisation was great, i enjoyed seeing and talking to a lot like-minded people, and the talks were really though-provoking this year. Setting sail to the next edition!

More ...
Uncategorized ruby gemsets rvm rails3 ruby on rails
rails 2 and 3 simultaneously: gemsets to the rescue

I am developing several rails projects. Some are still rails 2.3.9, and some are rails3. But using those at the same time, on the same machine can cause some trouble. I use bundles for all my projects, so the gem dependencies are managed. But still, somehow, when running my tests, i get into trouble. If i run spec, it complains that it is deprecated. Rubymine doesn't support running tests in my rails 2 project. The solution? Use gemsets! Gemsets are a feature from rvm, the Ruby Version Manager. But with gemsets rvm does more than just manage your ruby version! A gemset is a set of gems, which you can switch at will. So i am using a gemset per project. Creating a gemset is easy: [ruby] rvm gemset create some_name [/ruby] Using a gemset is equally simple: [ruby] rvm gemset use some_name [/ruby] Showing which gemset is in use: [ruby] rvm gemset name [/ruby] Showing the list of possible gemsets [ruby] rvm gemset list [/ruby] By default, you are using an unnamed gemset, which contains all the gems for your ruby-version. Per project you can create a new gemset. Per gemset you have to re-install your gems, but luckily, using bundler, that is as simple as doing bundle install. Now comes the good part:

  • Rubymine supports using gemsets, so you can select your gemset to work with, and each time your project is opened, it uses the correct gemset and the correct corresponding scripts (e.g. spec)
  • If you place a .rvmrc file in your project-folder containing rvm default@projecta, every time you cd into that folder it will select the default ruby and the projecta gemset. Awesome!
More ...
Uncategorized
in-place-editing in rails3

On-the-spot is a Rails3 compliant unobtrusive javascript in-place-editing plugin, using jEditable. As this jEditable depends on jQuery, you have to install that first. How to setup a fresh rails3 site with jquery is explained in this post. In short it is easy: [ruby] gem "jquery-rails" [/ruby] Run the installation task: [ruby] rails g jquery:install [/ruby]

Installation

Inside your Gemfile add the following: [ruby] gem "on_the_spot" [/ruby] Run the installation task: [ruby] rails g on_the_spot:install [/ruby] Inside your routes.rb you need to provide the following route: [ruby] resources :posts do collection do put :update_attribute_on_the_spot end end [/ruby] You need to do this for each controller that uses the on-the-spot editing (in this example for the PostsController). For the moment i do not know of any better solution, but i am always open for suggestions! Inside your application.html.haml you will need to add below the default javascripts: [ruby] = javascript_include_tag :on_the_spot [/ruby] or using erb, you write [ruby] < %= javascript_include_tag :on_the_spot %> [/ruby] That is all you need to do to start using it!

Usage

Inside your controller you write: [ruby] class YourController < ApplicationController can_edit_on_the_spot ... leave the rest of your controller alone ... end [/ruby] And inside your view you will have to specify the fields you want to be "editable" : [ruby] Username: <%= on_the_spot_edit @user, :name %> [/ruby] It should be as simple as that :)

Detailed options

The on_the_spot_edit also accepts options:

  • :type : :textarea or select (none means default edit)
  • :ok_text : the text for the ok-button
  • :cancel_text : the text for the cancel-button
  • :tooltip : the tooltip-text
  • :rows : for textarea, the number of rows, defaults to 5
  • :columns : for textarea, the number of columns, defaults to 40
  • :data : for select, the lookup-data, should be in an array of id-value pairs. E.g. [[1, 'ok'], [2, 'not ok'], [3, 'not decided']]. For the texts: if a text is not specified, the default is taken from the on_the_spot.en.yml (or your current language).

Examples

Edit field

[ruby] < %= on_the_spot_edit @user, :name %> [/ruby]

Textarea

[ruby] < %= on_the_spot_edit @user, :description, :type => :textarea, :rows => 10, :columns => 55 %> [/ruby]

Select-box

[ruby] < %= on_the_spot_edit @user, :rating, :type => :select, :data => [[1, 'good'], [2, 'mediocre'], [3, 'bad']] %> [/ruby]

Example project

There is an example rails3-project called on_the_spot_tester, where this is demonstrated in action. Let me know what you think.On-the-spot is a Rails3 compliant unobtrusive javascript in-place-editing plugin, using jEditable.

Installation

Inside your Gemfile add the following: [ruby] gem "on_the_spot" [/ruby] Run the installation task: [ruby] rails g on_the_spot:install [/ruby] Inside your routes.rb you need to provide the following route: [ruby] resources :posts do collection do post :update_attribute_on_the_spot end end [/ruby] You need to do this for each controller that uses the on-the-spot editing (in this example for the PostsController). For the moment i do not know of any better solution, but i am always open for suggestions! Inside your application.html.haml you will need to add below the default javascripts: [ruby] = javascript_include_tag :on_the_spot [/ruby] or using erb, you write [ruby] < %= javascript_include_tag :on_the_spot %> [/ruby] That is all you need to do to start using it!

Usage

Inside your controller you write: [ruby] class YourController < ApplicationController can_edit_on_the_spot ... leave the rest of your controller alone ... end [/ruby] And inside your view you will have to specify the fields you want to be "editable" : [ruby] Username: <%= on_the_spot_edit @user, :name %> [/ruby] It should be as simple as that :)

Detailed options

The on_the_spot_edit also accepts options:

  • :type : :textarea or select (none means default edit)
  • :ok_text : the text for the ok-button
  • :cancel_text : the text for the cancel-button
  • :tooltip : the tooltip-text
  • :rows : for textarea, the number of rows, defaults to 5
  • :columns : for textarea, the number of columns, defaults to 40
  • :data : for select, the lookup-data, should be in an array of id-value pairs. E.g. [[1, 'ok'], [2, 'not ok'], [3, 'not decided']]. For the texts: if a text is not specified, the default is taken from the on_the_spot.en.yml (or your current language).

Examples

Edit field

[ruby] < %= on_the_spot_edit @user, :name %> [/ruby]

Textarea

[ruby] < %= on_the_spot_edit @user, :description, :type => :textarea, :rows => 10, :columns => 55 %> [/ruby]

Select-box

[ruby] < %= on_the_spot_edit @user, :rating, :type => :select, :data => [[1, 'good'], [2, 'mediocre'], [3, 'bad']] %> [/ruby]

Example project

There is an example rails3-project called on_the_spot_tester, where this is demonstrated in action. Let me know what you think.

More ...
Uncategorized rspec2 rcov rails3 ruby on rails
getting rcov working with rspec 2.0.0.rc

I had troubles to get rcov working with the latest rspec2 release (since beta23 and now 2.0.0.rc). I got the same error every time: [bash] require': no such file to load -- spec\_helper (LoadError) [/bash] But luckily, somebody [found the problem](http://github.com/rspec/rspec-core/issues/issue/172) and it is extremely easy to fix. Just add -Ispecto your rcov task. Therake spec:rcovdoes not work for me (as it needs to be fixed). So i added my own task (add this code to the end ofRakefileor add in a seperate filercov.rakeinsidelib/tasks`) : [ruby] desc "Run all specs with rcov" RSpec::Core::RakeTask.new("test_cov") do |t| t.rcov = true t.rcov_opts = %w{--rails --include views -Ispec --exclude gems/,spec/,features/,seeds/} end [/ruby] and then you can run the task by typing [bash] > rake test_cov [/bash] in your rails root folder.

More ...
Uncategorized cross-browser css ie8
ie8 css can't find my id

I was having this weird problem in IE8. My page was rendered correctly in Chrome and Firefox, but not in IE8. This was a bit weird, as it was pretty standard stuff, i had some form-styling, something like this: [css] form#some_long_id4test .example { font-style: italic; } form#some_long_id4test>div { clear:left; padding:5px; } form#some_long_id4test label { width:160px; display:block; float:left; } form#some_long_id4test .field-el { float:left; display:block; } form#some_long_id4test input[type='submit'] { margin-right:160px; margin-top: 20px; height:25px; padding-left:15px; padding-right:15px; } form#some_long_id4test .field-el .field-with-errors { color:red; } form#some_long_id4test #feedback_container { clear:both; margin-top: 20px; display:block; } form#some_long_id4test #fix_button { margin-top:-10px; padding-left:165px; } [/css] Seems pretty obvious. Why didn't it work? After a long search, trying stuff out, trying to rewrite my css to get it working, i saw my error. Once found it was very obvious: in my ruby-view i had the following: [ruby] <% form_for @form, :html => {:multipart => true, :id => "some_long_id4test "} do |f| [/ruby] Wow! So my id is actually some_long_id4test with an added newline! Apparently IE8 thinks that some_long_id4test and some_long_id4test\n are not equal (which is not unlogical). It is sometimes so hard, confusing, difficult that all these browsers behave different.

More ...
Uncategorized jquery json jsonp rails3
rails3 doing cross-browser json

I have two rails applications that communicate together. In the first application i have the following method in my controller: [ruby] def delivery_status envelope = Envelope.find(params[: id]) render : json => envelope.to_json end [/ruby] which, if go to the url in my browser, nicely shows my the JSON. Seems ok. However, if i call this through jQuery, from my second application, using the following: [javascript] $(document).ready(function(){ $('.get_sms_details').live('click', function(event) { var el = $(this), data_url = el.attr('data-url'); $.ajax({ url: data_url, dataType: 'text', success: function(data, status, req) { alert('received with status' + status) alert('received json ' + data); } }); }); }); [/javascript] then the right url is hit with the correct parameters but data is always empty. I first tried using $.getJSON, then $.get to end at $.ajax. I am not sure but it seemed i was doing something wrong at server-side. The request looked fine inside firebug, but the response was always empty. Yet, i did not understand, if let the browser hit the same url, i got my json object. So how do you solve this? Well, i was reading the documentation of $.ajax, and there i found:

When data is retrieved from remote servers (which is only possible using the script or jsonp data types), the operation is performed using a script tag rather than an XMLHttpRequest object.

So, jsonp was the way, but how? First, i changed my jQuery code: [sourcecode language="javascript"] $(document).ready(function(){ $('.get_sms_details').live('click', function(event) { var el = $(this), data_url = el.attr('data-url'); data_url = data_url $.ajax({ url: data_url, dataType: 'jsonp', success: function(data) { envelope = data.envelope; alert('received envelope ' + data.envelope.id); } }); }); }); [/sourcecode] but then my server-side needed to be able to handle the jsonp. I handled that using the following code: [ruby] def delivery_status envelope = Envelope.find(params[:id]) render_json envelope.to_json(:include => [: deliveries, : log_lines]) end private # render json, but also allow JSONP and handle that correctly def render_json(json, options={}) callback, variable = params[:callback], params[:variable] logger.debug("render json or jsonp? Callback = #{callback}, Variable=#{variable}") response = begin if callback &amp;&amp; variable "var #{variable} = #{json};\n#{callback}(#{variable});" elsif variable "var #{variable} = #{json}" elsif callback "#{callback}(#{json});" else json end end render({:content_type => :js, :text => response}.merge(options)) end [/ruby] Where the render_json does all the dirty work for me :) I was somewhat expecting this to be standard inside rails3, and as Kevin Chiu pointed out in the comments, it is and much simpler at that: [ruby] def delivery_status envelope = Envelope.find(params[:id]) render :json => envelope.to_json(:include => [: deliveries, : log_lines]), :callback => params[:callback] end [/ruby] Awesome :)I have two rails applications that communicate together. In the first application i have the following method in my controller: [ruby] def delivery_status envelope = Envelope.find(params[: id]) render : json => envelope.to_json end [/ruby] which, if go to the url in my browser, nicely shows my the JSON. Seems ok. However, if i call this through jQuery, from my second application, using the following: [javascript] $(document).ready(function(){ $('.get_sms_details').live('click', function(event) { var el = $(this), data_url = el.attr('data-url'); $.ajax({ url: data_url, dataType: 'text', success: function(data, status, req) { alert('received with status' + status) alert('received json ' + data); } }); }); }); [/javascript] then the right url is hit with the correct parameters but data is always empty. I first tried using $.getJSON, then $.get to end at $.ajax. I am not sure but it seemed i was doing something wrong at server-side. The request looked fine inside firebug, but the response was always empty. Yet, i did not understand, if let the browser hit the same url, i got my json object. So how do you solve this? Well, i was reading the documentation of $.ajax, and there i found:

When data is retrieved from remote servers (which is only possible using the script or jsonp data types), the operation is performed using a script tag rather than an XMLHttpRequest object.

So, jsonp was the way, but how? First, i changed my jQuery code: [sourcecode language="javascript"] $(document).ready(function(){ $('.get_sms_details').live('click', function(event) { var el = $(this), data_url = el.attr('data-url'); data_url = data_url $.ajax({ url: data_url, dataType: 'jsonp', success: function(data) { envelope = data.envelope; alert('received envelope ' + data.envelope.id); } }); }); }); [/sourcecode] but then my server-side needed to be able to handle the jsonp. I handled that using the following code: [ruby] def delivery_status envelope = Envelope.find(params[:id]) render_json envelope.to_json(:include => [: deliveries, : log_lines]) end private # render json, but also allow JSONP and handle that correctly def render_json(json, options={}) callback, variable = params[:callback], params[:variable] logger.debug("render json or jsonp? Callback = #{callback}, Variable=#{variable}") response = begin if callback && variable "var #{variable} = #{json};\n#{callback}(#{variable});" elsif variable "var #{variable} = #{json};" elsif callback "#{callback}(#{json});" else json end end render({:content_type => :js, :text => response}.merge(options)) end [/ruby] Where the render_json does all the dirty work for me :) I was somewhat expecting this to be standard inside rails3, but apparently it isn't. Are there any better ways to handle this?

More ...
Uncategorized rspec2 watchr rails3
rspec2 using watchr instead of autotest

I was having troubles with autotest, and not really finding a good solution. But i noticed that inside rspec2 they were using watchr. Watchr is a very generic gem that will allow you to watch a set of files and take action when something changes. That sounds great, but maybe a bit too general :) So, it appears you need a script to run your rspec2 continuously using watchr. Luckily i did find some examples, which allowed my to brew my own (save in your rails-root as specs.watchr): [ruby wraplines="false"] # adapted from http://github.com/rspec/rspec-rails/blob/master/specs.watchr # Run me with: # # $ watchr specs.watchr # -------------------------------------------------- # Convenience Methods # -------------------------------------------------- def all_spec_files Dir['spec/**/*_spec.rb'] end def run_spec_matching(thing_to_match) matches = all_spec_files.grep(/#{thing_to_match}/i) if matches.empty? puts "Sorry, thanks for playing, but there were no matches for #{thing_to_match}" else run matches.join(' ') end end def run(files_to_run) puts("Running: #{files_to_run}") system("rspec -c #{files_to_run}") no_int_for_you end def run_all_specs run(all_spec_files.join(' ')) end # -------------------------------------------------- # Watchr Rules # -------------------------------------------------- watch('^spec/(.*)_spec.rb') { |m| run_spec_matching(m[1]) } watch('^app/(.*).rb') { |m| run_spec_matching(m[1]) } watch('^app/(.*).haml') { |m| run_spec_matching(m[1]) } watch('^lib/(.*).rb') { |m| run_spec_matching(m[1]) } watch('^spec/spec_helper.rb') { run_all_specs } watch('^spec/support/.*.rb') { run_all_specs } # -------------------------------------------------- # Signal Handling # -------------------------------------------------- def no_int_for_you @sent_an_int = nil end Signal.trap 'INT' do if @sent_an_int then puts " A second INT? Ok, I get the message. Shutting down now." exit else puts " Did you just send me an INT? Ugh. I'll quit for real if you do it again." @sent_an_int = true Kernel.sleep 1.5 run_all_specs end end [/ruby] It even seems to be quicker than autotest, but not sure if that is just wishful thinking.

More ...
Uncategorized authlogic rails3
authlogic on rails3

If you want to get authlogic working in a fresh rails 3 project, it will take a bit more steps than devise. This has everything to do with vision: authlogic only claims to deliver you the backend, allowing itself to remain more stable and to easily replace other authentication libraries (even your home-brewn). That seems a great philosophy, but starting from scratch involves more steps. First you need to setup a clean rails3 project, as i described before.

Install the gem

As simple as adding the following line to your Gemfile : [ruby] gem "authlogic" gem "rails3-generators" [/ruby] (note: you need the rails3-generators to include the needed generators). Then run bundle install or bundle update.

Create the UserSession

[ruby] rails g authlogic:session UserSession [/ruby] This would be all, but in my installation it was not enough. Something inside rails3 broke the authlogic session. But the fix, luckily, is pretty easy: you have to add the to_key function. So your complete UserSession model will look as follows: [ruby] class UserSession < Authlogic::Session::Base def to_key new_record? ? nil : [self.send(self.class.primary_key)] end end [/ruby]

Create the User

If you do not yet have a User model, and i am assuming you don't, you need to [ruby] rails g model User [/ruby] For now, this is an empty model. You will need to fill in your migration to create the model correctly. [ruby wraplines="false"] class CreateUsers < ActiveRecord::Migration def self.up create_table :users do |t| t.string :login, :null => false t.string :email, :null => false t.string :crypted_password, :null => false t.string :password_salt, :null => false t.string :persistence_token, :null => false #t.string :single_access_token, :null => false # optional, see Authlogic::Session::Params #t.string :perishable_token, :null => false # optional, see Authlogic::Session::Perishability # magic fields (all optional, see Authlogic::Session::MagicColumns) t.integer :login_count, :null => false, :default => 0 t.integer :failed_login_count, :null => false, :default => 0 t.datetime :last_request_at t.datetime :current_login_at t.datetime :last_login_at t.string :current_login_ip t.string :last_login_ip t.timestamps end add_index :users, ["login"], :name => "index_users_on_login", :unique => true add_index :users, ["email"], :name => "index_users_on_email", :unique => true add_index :users, ["persistence_token"], :name => "index_users_on_persistence_token", :unique => true end def self.down drop_table :users end end [/ruby] Now we still have to add some code to the User model : [ruby] class User < ActiveRecord::Base acts_as_authentic end [/ruby]

ApplicationController

We need to add some generic code to our applicationcontroller to persist the sessions, to check whether a user is required and do the correct redirects when needed. [ruby] class ApplicationController < ActionController::Base protect_from_forgery helper_method :current_user_session, :current_user private def current_user_session logger.debug "ApplicationController::current_user_session" return @current_user_session if defined?(@current_user_session) @current_user_session = UserSession.find end def current_user logger.debug "ApplicationController::current_user" return @current_user if defined?(@current_user) @current_user = current_user_session && current_user_session.user end def require_user logger.debug "ApplicationController::require_user" unless current_user store_location flash[:notice] = "You must be logged in to access this page" redirect_to new_user_session_url return false end end def require_no_user logger.debug "ApplicationController::require_no_user" if current_user store_location flash[:notice] = "You must be logged out to access this page" redirect_to account_url return false end end def store_location session[:return_to] = request.request_uri end def redirect_back_or_default(default) redirect_to(session[:return_to] || default) session[:return_to] = nil end end [/ruby]

UserSessionsController

[ruby] rails g controller UserSessions new [/ruby] and fill the controller with the correct code: [ruby] class UserSessionsController < ApplicationController before_filter :require_no_user, :only => [:new, :create] before_filter :require_user, :only => :destroy def new @user_session = UserSession.new end def create @user_session = UserSession.new(params[:user_session]) if @user_session.save flash[:notice] = "Login successful!" redirect_back_or_default users_url else render :action => :new end end def destroy current_user_session.destroy flash[:notice] = "Logout successful!" redirect_back_or_default new_user_session_url end end [/ruby] and of course that also needs a view, in new.html.haml[ruby] %h1 Login = form_for @user_session, :url => {:action => "create"} do |f| = f.error_messages %div = f.label :login = f.text_field :login %div = f.label :password = f.password_field :password %div = f.check_box :remember_me = f.label :remember_me %div = f.submit "Login" [/ruby] We want to use the f.error_message, but that is now removed from Rails3 and we need to install a plugin instead: [ruby] rails plugin install git://github.com/rails/dynamic_form.git [/ruby] We also need to define, inside config/routes.rb. You will see that the generator will have added the route get 'user_sessions/new', but that is not enough. You will have to add: [ruby] resources :user_sessions match 'login' => "user_sessions#new", :as => :login match 'logout' => "user_sessions#destroy", :as => :logout [/ruby]

Restrict access

Suppose you now have some other controller, e.g. HomeController, then restricting acces is straightforward: [ruby] rails g controller home index [/ruby] [ruby] class HomeController < ApplicationController before_filter :require_user def index end end [/ruby] The method require_user, defined in ApplicationController, will check if there is a user logged on, and if not redirect to the login-page. If we now delete the index.html inside your public folder, and we add the following at the bottom of config/routes.rb[ruby] root :to => 'home#index' [/ruby] Now we can start our application, and it should redirect to our login-page. But of course, since we have no users for now, we can't login just yet. To allow testing, you fire up your console: [bash] $ rails c Loading development environment (Rails 3.0.0.rc) ruby-1.9.2-p0 > User.create(:login => 'test', :email => 'test@tester.com', :password => 'test123', :password_confirmation => 'test123') => #<User id: 1, login: "test", email: "test@tester.com", crypted_password: "0129d9733b7912017e37a50263901488da90e127e6fd1ae6081...", password_salt: "ygufdflWkJQZGhbsGyia", persistence_token: "957788609b2067092dd3852b01613d53f96d0692ce5131174ca...", login_count: 0, failed_login_count: 0, last_request_at: nil, current_login_at: nil, last_login_at: nil, current_login_ip: nil, last_login_ip: nil, created_at: "2010-08-29 14:11:04", updated_at: "2010-08-29 14:11:04"> ruby-1.9.2-p0 > [/bash] Now you should be able to login using this user. For completeness, you should add some links to your application-view to allow logging in and out: [ruby] #user_nav - if current_user = "Signed in as #{current_user.email}. Not you?" = link_to "Sign out", logout_path - else = link_to "Sign in", new_user_session_path [/ruby] This would get your rails3 project started. The next steps would be to add some user management, or allowing users to sign up themselves, and maybe add some roles to limit certain users access if needed. Now, to contrast this with devise: i now have a bunch of code in my application that actually is not specific to my code, but is also completely not tested. I will provide example rspec tests for this later. Actually, the problem with this scenario as outlined, is it is one big bang. You should start little, declaring things you need to be able to do, and work in little steps to get there. But this scenario is intended as a draft to see how you could get there. Now, in your real application, you should start with the tests, and then from this example you now how you can implement it.If you want to get authlogic working in a fresh rails 3 project, it will take a bit more steps than devise. This has everything to do with vision: authlogic only claims to deliver you the backend, allowing itself to remain more stable and to easily replace other authentication libraries (even your home-brewn). That seems a great philosophy, but starting from scratch involves more steps. First you need to setup a clean rails3 project, as i described before.

Install the gem

As simple as adding the following line to your Gemfile : [ruby] gem "authlogic" [/ruby] and then run bundle install or bundle update.

Create the UserSession

[ruby] rails g authlogic:session UserSession [/ruby] This would be all, but in my installation it was not enough. Something inside rails3 broke the authlogic session. But the fix, luckily, is pretty easy: you have to add the to_key function. So your complete UserSession model will look as follows: [ruby] class UserSession < Authlogic::Session::Base def to_key new_record? ? nil : [self.send(self.class.primary_key)] end end [/ruby]

Create the User

If you do not yet have a User model, and i am assuming you don't, you need to [ruby] rails g model User [/ruby] For now, this is an empty model. You will need to fill in your migration to create the model correctly. [ruby wraplines="false"] class CreateUsers < ActiveRecord::Migration def self.up create_table :users do |t| t.string :login, :null => false t.string :email, :null => false t.string :crypted_password, :null => false t.string :password_salt, :null => false t.string :persistence_token, :null => false #t.string :single_access_token, :null => false # optional, see Authlogic::Session::Params #t.string :perishable_token, :null => false # optional, see Authlogic::Session::Perishability # magic fields (all optional, see Authlogic::Session::MagicColumns) t.integer :login_count, :null => false, :default => 0 t.integer :failed_login_count, :null => false, :default => 0 t.datetime :last_request_at t.datetime :current_login_at t.datetime :last_login_at t.string :current_login_ip t.string :last_login_ip t.timestamps end add_index :users, ["login"], :name => "index_users_on_login", :unique => true add_index :users, ["email"], :name => "index_users_on_email", :unique => true add_index :users, ["persistence_token"], :name => "index_users_on_persistence_token", :unique => true end def self.down drop_table :users end end [/ruby] Now we still have to add some code to the User model : [ruby] class User < ActiveRecord::Base acts_as_authentic end [/ruby]

ApplicationController

We need to add some generic code to our applicationcontroller to persist the sessions, to check whether a user is required and do the correct redirects when needed. [ruby] class ApplicationController < ActionController::Base protect_from_forgery helper_method :current_user_session, :current_user private def current_user_session logger.debug "ApplicationController::current_user_session" return @current_user_session if defined?(@current_user_session) @current_user_session = UserSession.find end def current_user logger.debug "ApplicationController::current_user" return @current_user if defined?(@current_user) @current_user = current_user_session && current_user_session.user end def require_user logger.debug "ApplicationController::require_user" unless current_user store_location flash[:notice] = "You must be logged in to access this page" redirect_to new_user_session_url return false end end def require_no_user logger.debug "ApplicationController::require_no_user" if current_user store_location flash[:notice] = "You must be logged out to access this page" redirect_to account_url return false end end def store_location session[:return_to] = request.request_uri end def redirect_back_or_default(default) redirect_to(session[:return_to] || default) session[:return_to] = nil end end [/ruby]

UserSessionsController

[ruby] rails g controller UserSessions new [/ruby] and fill the controller with the correct code: [ruby] class UserSessionsController < ApplicationController before_filter :require_no_user, :only => [:new, :create] before_filter :require_user, :only => :destroy def new @user_session = UserSession.new end def create @user_session = UserSession.new(params[:user_session]) if @user_session.save flash[:notice] = "Login successful!" redirect_back_or_default users_url else render :action => :new end end def destroy current_user_session.destroy flash[:notice] = "Logout successful!" redirect_back_or_default new_user_session_url end end [/ruby] and of course that also needs a view, in new.html.haml[ruby] %h1 Login = form_for @user_session, :url => {:action => "create"} do |f| = f.error_messages %div = f.label :login = f.text_field :login %div = f.label :password = f.password_field :password %div = f.check_box :remember_me = f.label :remember_me %div = f.submit "Login" [/ruby] We want to use the f.error_message, but that is now removed from Rails3 and we need to install a plugin instead: [ruby] rails plugin install git://github.com/rails/dynamic_form.git [/ruby] We also need to define, inside config/routes.rb. You will see that the generator will have added the route get 'user_sessions/new', but that is not enough. You will have to add: [ruby] resources :user_sessions match 'login' => "user_sessions#new", :as => :login match 'logout' => "user_sessions#destroy", :as => :logout [/ruby]

Restrict access

Suppose you now have some other controller, e.g. HomeController, then restricting acces is straightforward: [ruby] rails g controller home index [/ruby] [ruby] class HomeController < ApplicationController before_filter :require_user def index end end [/ruby] The method require_user, defined in ApplicationController, will check if there is a user logged on, and if not redirect to the login-page. If we now delete the index.html inside your public folder, and we add the following at the bottom of config/routes.rb[ruby] root :to => 'home#index' [/ruby] Now we can start our application, and it should redirect to our login-page. But of course, since we have no users for now, we can't login just yet. To allow testing, you fire up your console: [bash] $ rails c Loading development environment (Rails 3.0.0.rc) ruby-1.9.2-p0 > User.create(:login => 'test', :email => 'test@tester.com', :password => 'test123', :password_confirmation => 'test123') => #<User id: 1, login: "test", email: "test@tester.com", crypted_password: "0129d9733b7912017e37a50263901488da90e127e6fd1ae6081...", password_salt: "ygufdflWkJQZGhbsGyia", persistence_token: "957788609b2067092dd3852b01613d53f96d0692ce5131174ca...", login_count: 0, failed_login_count: 0, last_request_at: nil, current_login_at: nil, last_login_at: nil, current_login_ip: nil, last_login_ip: nil, created_at: "2010-08-29 14:11:04", updated_at: "2010-08-29 14:11:04"> ruby-1.9.2-p0 > [/bash] Now you should be able to login using this user. For completeness, you should add some links to your application-view to allow logging in and out: [ruby] #user_nav - if current_user = "Signed in as #{current_user.email}. Not you?" = link_to "Sign out", logout_path - else = link_to "Sign in", new_user_session_path [/ruby] This would get your rails3 project started. The next steps would be to add some user management, or allowing users to sign up themselves, and maybe add some roles to limit certain users access if needed. Now, to contrast this with devise: i now have a bunch of code in my application that actually is not specific to my code, but is also completely not tested. I will provide example rspec tests for this later. Actually, the problem with this scenario as outlined, is it is one big bang. You should start little, declaring things you need to be able to do, and work in little steps to get there. But this scenario is intended as a draft to see how you could get there. Now, in your real application, you should start with the tests, and then from this example you now how you can implement it.

More ...
Uncategorized etsyhacks fast_tagger
etsy fast_tagger v2

There used to be a greasemonkey script fast_tagger which allowed an etsy seller to add all tags at once when creating or editing an item. Since april of this year this script was broken. My girlfriend has had a shop for a couple of years and missed the functionality from etsyhacks dearly. Apparently Ian, the person behind etsyhacks, is now working for etsy himself and isn't maintaining the scripts anymore. So, without further ado: you can download the fast_tagger_2 script to install. It should operate in the same way as before.

Installation instructions:

  • install firefox and greasemonkey if you haven't done so already.
  • once Greasemonkey is installed and running, follow this link.
  • The "fast tagger 2" script will be downloaded, and Greasemonkey will ask if you want to install it. Assuming you trust me, click "Install".

Operation

When you create a new listing and go to the tags page, or when you edit the tags of an existing listing, you will see that a new box is now visible. Inside this box, you can enter tags, comma separated and ...

  • click "add tags" to add the tags to the listing
  • click "replace tags" to remove any existing tags and replace them with your current ones.
  • or you can click "delete tags" to remove any existing tags. [UPDATE] I have been in touch with Ian, and he assured me he is going to continue supporting etsyhacks, only that he currently was swamped in work. I leave this script for whoever it could help in the meantime.
More ...
Uncategorized pgadmin postgresql ubuntu
fixing pgadmin3

When i try to run pgadmin3 on my ubuntu 10.04 box i now get the following error: [bash] $ pgadmin3 pgadmin3: relocation error: pgadmin3: symbol _ZN21wxMemoryFSHandlerBase19AddFileWithMimeTypeERK8wxStringPKvjS2_, version WXU_2.8 not defined in file libwx_baseu-2.8.so.0 with link time reference [/bash] I assume it has something to do with one of the latest updates. I run all the updates always when they are proposed, but i am not really sure this always is a good idea. When googling the error i found a lot of links, and ultimately, also the fix: [bash] apt-get source pgadmin3 sudo apt-get install debhelper libpq-dev libwxgtk2.8-dev libxml2-dev libxslt1-dev autotools-dev devscripts cd pgadmin3-1.10.2 dpkg-buildpackage sudo dpkg -i ../pgadmin3_1.10.2-1_i386.deb [/bash]

More ...
Uncategorized delayed_job rails3
setting up delayed_job in rails3

If you want to run longrunning jobs in the background, one very easy solution is using delayed_job. One other very interesting alternative is resque, but it seemed harder to setup (it uses redis), and delayed_job seemed to be just right for my needs. In short (from the redis documentation) : Choose Resque if:

  • You need multiple queues
  • You don't care / dislike numeric priorities
  • You don't need to persist every Ruby object ever
  • You have potentially huge queues
  • You want to see what's going on
  • You expect a lot of failure / chaos
  • You can setup Redis
  • You're not running short on RAM Choose DelayedJob if:
  • You like numeric priorities
  • You're not doing a gigantic amount of jobs each day
  • Your queue stays small and nimble
  • There is not a lot failure / chaos
  • You want to easily throw anything on the queue
  • You don't want to setup Redis And in my case: a short list of outstanding jobs, i am using a database already, not sure about redis, performance is not that important, i do not expect a lot of failure, i do not want to setup redis (yet :). So to use the delayed_job gem in a rails3 you will need to use the code from github (as the gem version 2.0.3 is not yet rails3 compatible). Add the following line to your Gemfile : [ruby] gem "delayed_job", :git => 'git://github.com/collectiveidea/delayed_job.git' [/ruby] and then run [bash] bundle install [/bash] Because i am using ActiveRecord, i can just use the generator, that will create the table and add the script to run a worker: [ruby] rails generate delayed_job rake db:migrate [/ruby] Include the rake tasks from delayed-jobs into your Rakefile: [ruby] begin require 'delayed/tasks' rescue LoadError STDERR.puts "Run bundle:install to install delayed_job" end [/ruby] Create an initializer file delayed_jobs_config.rb and write [ruby] # config/initializers/delayed_job_config.rb Delayed::Worker.destroy_failed_jobs = false #Delayed::Worker.sleep_delay = 60 #Delayed::Worker.max_attempts = 3 Delayed::Worker.max_run_time = 5.minutes [/ruby] If you are happy with the defaults, you can leave it. For me it was important to keep a record of the jobs that failed. Then you can just delay any function-call like this: [ruby] envelope.delay.query_status(delivery) [/ruby] Just adding the .delay does all the magic for you! And starting a worker is as simple as [ruby] rake jobs:work [/ruby]
More ...
Uncategorized
rspec2 and rails3: controller testing with devise

Devise seems to become the new authentication standard in rails land. Also the last version of the gem is for rails3, so it is very easy to start using it. If you are using rspec2 for your tests, it is also really easy to test your controllers that are authenticated by devise. Suppose you have a DoStuffController like this: [ruby] class DoStuffController < ApplicationController before_filter :authenticate_user! def index end end [/ruby] You need to test it as follows in do_stuff_controller_spec.rb: [ruby] require 'spec_helper' describe DoStuffController do include Devise::TestHelpers before (:each) do @user = Factory.create(:user) sign_in @user end describe "GET 'index'" do it "should be successful" do get 'index' response.should be_success end end end [/ruby] And in your factory, users.rb: [ruby] Factory.define :user do |f| f.email "bla@blabla.com" f.password "blax123" end [/ruby] I am not testing the fact that the redirect will work, under the assumption that that is specific devise functionality which i assume to be tested and working.

More ...
Uncategorized ajax unobtrusive-javascript jquery rails3
handling ajax callbacks in rails3 using jquery

Previously, in rails 2.3.8 i used the prototype-helpers link_to_remote and form_remote_for (amongst others). These had the option to add callbacks as follows: [ruby] link_to_remote "Add to cart", :url => { :action => "add", :id => product.id }, :update => { :success => "cart", :failure => "error" } [/ruby] (an example from the documentation). This example would, upon success update the html-element with class "cart", and upon failure the class "error". The possible callbacks were:

  • :loading: Called when the remote document is being loaded with data by the browser.
  • :loaded: Called when the browser has finished loading the remote document.
  • :interactive: Called when the user can interact with the remote document, even though it has not finished loading.
  • :success: Called when the XMLHttpRequest is completed, and the HTTP status code is in the 2XX range.
  • :failure: Called when the XMLHttpRequest is completed, and the HTTP status code is not in the 2XX range.
  • :complete : Called when the XMLHttpRequest is complete (fires after success/failure if they are present). Now the modus operandi has changed, instead we write: [ruby] link_to "Add to cart", :url => {:action => "add", :id => product.id}, :remote => true [/ruby] and it seems there is no option to set the callbacks anymore. Instead of a normal html, we now render javascript, like this (in jquery) : [ruby] $('.cart').replaceWith(<%= escape_javascript(render :partial => 'cart') %>) [/ruby] But how do you handle an error situation? Do i handle it in my controller, and use seperate views? It would seem useful to me to somehow be able to mimic the behaviour we had before. Luckily in this article I was able to find the solution. I had already found that in rails.js the following callbacks were checked:
  • ajax:beforeSend : triggered before executing the AJAX request
  • ajax:success : triggered after a successful AJAX request
  • ajax:complete : triggered after the AJAX request is complete, regardless the status of the response
  • ajax:error : triggered after a failed AJAX request, as opposite to ajax:success But i had no idea how to provide these callbacks. The javascript should be unobtrusive, so this coupling is not done straight in the HTML anymore. From the same article i found a very clear example how to solve this. Take the following Rails 2.3.8 code : [ruby] <% form_remote_tag :url => { :action => 'run' }, :id => "tool-form", :update => { :success => "response", :failure => "error" }, :loading => "$('#loading').toggle()", :complete => "$('#loading').toggle()" %> [/ruby] That translates to this in Rails3 : [ruby] <% form_tag url_for(:action => "run"), :id => "tool-form", :remote => true do %> [/ruby] and inside some javascript (application.js), you bind the events [javascript] jQuery(function($) { // create a convenient toggleLoading function var toggleLoading = function() { $("#loading").toggle() }; $("#tool-form") .bind("ajax:beforeSend", toggleLoading) .bind("ajax:complete", toggleLoading) .bind("ajax:success", function(data, status, xhr) { $("#response").html(status); }); }); [/javascript] For completeness, here is a list of the events and their expected parameters: [javascript] .bind('ajax:beforeSend', function(xhr, settings) {}) .bind('ajax:success', function(data, status, xhr) {}) .bind('ajax:complete', function(xhr, status) {}) .bind('ajax:error', function(xhr, status, error) {}) [/javascript] [UPDATED 7/2/2012] Updated to reflect the new event-names. :loading was renamed to :beforeSend, and :failure was renamed to :error.
More ...
Uncategorized haml jquery rails3 ruby on rails
rails 3 and haml and unobtrusive javascript

I have created a Rails3 application, started with Haml/Sass and finding it awesome. I am also trying to do unobtrusive javascript. Before, in Rails 2.3, I would have expected a remote-form to have an :update attribute, where you could specify a selector where the response of the remote method would be rendered. Now it needs to be done differently: my controller function will render a ".js" view, which will do the necessary actions itself. This makes it more library agnostic, and i prefer jquery. So for instance i have a controller action "search", and i have a corresponding "search.js.erb" as follows: [ruby] $('.grid').replaceWith('<%= escape_javascript(render :partial => 'list') %>') [/ruby] This works. It will replace the html of the element with a class grid with the html from my rendered partial. I started out trying to achieve that in HAML, and failed at first. So first i created the above ERB code which worked. Now my task seemed simpler: translate this seemingly simple line to HAML. My first naive approach was to do the following: [ruby] = "$('.grid').replaceWith('#{escape_javascript(render :partial => 'list')}')" [/ruby] But this just places the html as a readable string inside my page. Even i do something simple like [ruby] = "$('.grid').replaceWith('<h3>Text</h3>') [/ruby] i see the actual characters [ruby] <h3>Text</h3> [/ruby] and not the markup. But i need to contain the javascript inside the double-quotes or haml will not recognise it, and can not interpolate my ruby there. After looking through the HAML reference, actually the solution was incredible simple. Once more. [ruby] != "$('.grid').replaceWith('#{escape_javascript(render :partial => 'list')}')" [/ruby] The != unescapes HTML (as opposed to the standard =). This is exactly what we need of course.

More ...
Uncategorized haml jquery rspec2 rails3
starting a new rails3 site using rspec2 and jquery

With Rails3 i can create a project fully cut to my needs. I will write it down here, just so i remember it well and hopefully it will help some of you too. In my projects, i want to use haml, rspec2, factory-girl, jquery, ... We need a few steps to complete this.

Create the project

For starters, create the folder, without Test::Unit (-T) and without prototype (-J). [ruby] rails new test-project -T -J [/ruby] You could also specify the database, using option -d, [--database=DATABASE], with the following options: `

mysql oracle postgresql sqlite3 (default) frontbase ibm_db`

Specify needed gems

We also have to specify the gems we need. To do that, we need to edit the Gemfile which is located in the root of your project. My file looks as follows: [ruby] source 'http://rubygems.org' gem 'rails', '3.0.0.rc' # Bundle edge Rails instead: # gem 'rails', :git => 'git://github.com/rails/rails.git' #gem 'pg' gem 'sqlite3-ruby', :require => 'sqlite3' # Use unicorn as the web server # gem 'unicorn' # Deploy with Capistrano # gem 'capistrano' # To use debugger # gem 'ruby-debug' gem 'rails3-generators' gem "bson_ext" gem "haml" gem "haml-rails" gem "jquery-rails" gem "rcov" # we need this here, see http://blog.davidchelimsky.net/2010/07/11/rspec-rails-2-generators-and-rake-tasks/ group :development, :test do gem "rspec-rails", ">= 2.0.0.beta.18" end # test-environment gems group :test, :spec, :cucumber do gem "factory_girl_rails" gem "rspec", ">= 2.0.0.beta.18" gem "remarkable", ">=4.0.0.alpha2" gem "remarkable_activemodel", ">=4.0.0.alpha2" gem "remarkable_activerecord", ">=4.0.0.alpha2" gem "capybara" gem "cucumber" gem "database_cleaner" gem "cucumber-rails" end [/ruby] Run bundle install to install all needed gems.

Use wanted generators

Then now edit the file application.rb, located in the config folder, and add the following lines: [ruby] # Configure generators values config.generators do |g| g.test_framework :rspec, :fixture => true g.fixture_replacement :factory_girl, :dir=>"spec/factories" end [/ruby] This will make sure that the generators use our own defaults. So now when you type [ruby] rails generate model TestModel name:string rails generate controller TestModel [/ruby] and it will create haml views, rspec tests, and factories. Awesome :)

Prepare to use jquery!

To start using jQuery in Rails3, you have to first get the rails.js specifically for jQuery (stored in the jquery-ujs project). Save it in your public\javascripts directory. Download jquery, and make sure to include both in your application layout or view. In HAML it would look something like. [ruby] = javascript_include_tag 'jquery.min.js' = javascript_include_tag 'rails' [/ruby] BUT: there is a quicker route! We also installed the gem jqeury-rails, which has the following generator if we want to install jquery in one go! [ruby] rails g jquery:install #--ui to enable jQuery UI [/ruby] Rails requires an authenticity token to do form posts back to the server. This helps protect your site against CSRF attacks. In order to handle this requirement the driver looks for two meta tags that must be defined in your page's head. Luckily rails makes it easier for us, again, and we just need to include csrf_meta_tag somewhere inside your page's head (rails3 does this default in your application.html.erb). Be sure to include it in your haml code too. An example application.html.haml would look like this: [ruby] !!! Strict %html{ "xml:lang" => "en", :lang => "en", :xmlns => "http://www.w3.org/1999/xhtml" } %head %title== Test-project #{@page_title} %meta{'http-equiv' => "Content-Type", :content => "text/html; charset=utf-8"} %link{ :href => "/favicon.ico", :rel => "shortcut icon" } = stylesheet_link_tag :all = javascript_include_tag 'jquery-1.4.2.min.js', 'rails.js', 'application.js', :cache => 'all_1' = csrf_meta_tag = yield :local_javascript = yield :head %body #banner =image_tag 'your-logo.png' .the-title %h1 %p Your application #navigation #container #sidebar #contents =yield #footer == © 2010 your company [/ruby]

Done! :)

More ...
Uncategorized ruby rspec
awesome rspec hints and tips

Read "Pure Rspec", a great slideshow by Jon "Lark" Larkowski. It contains some really cool rspec tips and hints, how to write your tests more readable and compact.

More ...
Uncategorized ruby jeweler gem
creating a new gem

I found a few articles that describe easier ways to create a gem. From the entire list of available tools, i want to highlight two: jeweler and (Mr.) bones. Mr. bones seems a very configurable route to take: it allows you to define your own skeletons (application templates) or use those of others, e.g. bort. On the other hand it also seems to do more than i need. Jeweler seems to be very specific in what i does for you, allowing to create a gem very easy and manage your gem locally. Also it is very configurable, so you can immediately create rspec and cucumber folders, fill the wanted rake tasks. It is awesome, and makes starting really easy. So i executed the following command: [ruby] D:\work-git\ruby>jeweler --rspec --cucumber --reek --roodi --create-repo word_automator create .gitignore create Rakefile create LICENSE create README.rdoc create .document create lib create lib/word_automator.rb create spec create spec/spec_helper.rb create spec/word_automator_spec.rb create spec/spec.opts create features create features/word_automator.feature create features/support create features/support/env.rb create features/step_definitions create features/step_definitions/word_automator_steps.rb Jeweler has prepared your gem in word_automator Jeweler has pushed your repo to http://github.com/nathanvda/word_automator [/ruby] which as you can tell created a folder, rspec and cucumber place-holders, prepared my gem and pushed my repository to github! Wow :) Now fill the gem with code :)

More ...
Uncategorized view testing rspec2 rails3 rails rspec
rails3 and rspec2 view testing

Testing the view in Rspec is just great. It allows you to test the view in total isolation. It just does the render. Make sure you set up some data that is needed, and then check if all areas are available for instance. Now for Rspec2 a lot of things have changed:

  • have_tag is no longer supported: use have_selector from webrat instead
  • response is deprecated, use rendered instead
  • the assignment has changed, write assign(:events, [stub_model(Event)]) instead of using the old assigns[:events]=[...] notation Let me show you a full example, because that always makes things clearer: [ruby wraplines="false"] require 'spec_helper' DUMMY_NUMBER="dummy number" DUMMY_MESSAGE="dummy message" describe "envelopes/index.html.haml" do before (:each) do envelope = mock_model(Envelope) envelope.should_receive(:destination).and_return(DUMMY_NUMBER) envelope.should_receive(:message).and_return(DUMMY_MESSAGE) assign(:envelopes, [envelope]) render end it ("has a h1") { rendered.should have_selector('h1')} it ("shows the correct title") { rendered.should contain(t('envelopes.list.title')) } it ("have a table-grid") { rendered.should have_selector('.grid') } it ("shows the destination") { rendered.should contain(DUMMY_NUMBER) } it ("shows the message") { rendered.should contain(DUMMY_MESSAGE) } end [/ruby]
More ...
Uncategorized refactoring rspec
rspec testing using macros

[ruby wraplines="false"] STATUS_EXPECTED_TRANSLATIONS = [[inp1, outp1], ... ] it "should translate received statuses" do STATUS_EXPECTED_TRANSLATIONS.each do |tr| controller.send('translate_status_to_envelope_status', tr[0]).should == tr[1] end end [/ruby] While this is short and DRY, a problem arises when one of the values in my look-up table does not behave as expected anymore. The complete test will fail, without having any clue which value caused it to fail. That is not good! But luckily, in rspec there exists the option to use macros. It is a kind of templates for tests. Using macros i could write this much cleaner, and generate my test-clauses on the fly. [ruby wraplines="false"] STATUS_EXPECTED_TRANSLATIONS.each do |tr| it "should translate #tr[0]} to success" do controller.send('translate_status_to_envelope_status', tr[0]).should == tr[1] end end [/ruby] Now if a test fails, i know which i value need to check, because i will get an appropriate error-message.

More ...