It seems like it happened yesterday. I arrived at the flat in a crumbling tenement located in the Old Town of Wrocław and my journey into the Ruby world began. The community was already large and very active, so I wouldn’t call myself Columb, but—at least—I can tell you about my first year experience and share both bright and dark sides of the story.
Posts in the category “Ruby”
Validate gem checksums on Capistrano deployment
RubyGems.org has been compromised.
The exploit could potentially tamper with some gems, therefore all of them are assumed to be unsafe to use at the moment. Which means that any deployment involving downloads of any gems from RubyGems.org should be carried out with extreme caution. All downloaded gems have to be verified in order to remain on the safe side.
From Monterail with Love #2
It’s time for New Year’s edition of our summary of the content found in couple of the last weeks. While you are getting back into ordinary flow you can read about Vim, objects in Rails and improve your hacker’s toolbox. Enjoy!
Vim religion
As you could notice I’m big fan of Vim and I promote it intensively among colleagues. Even if you think that it is not for newbies or you feel uncomfortable using it, you should be aware that you’re probably using an editor which is not designed for programmers. So, if you are interested in topic, just know that you can build an IDE around Vim, arm it in powerful search feature or configure for writing… prose.
Don’t forget to read our review of the awesome Practical Vim book.
At last but not least, if you’re still not convinced, check this amazing tip how Vim can save your time.
Object Oriented Rails
There are a lot of interesting ideas how to improve OO approach in Rails. Xavier Shay shared with his solution for form objects and completely redesigned idea of controllers. Chris Flipse shows how you can isolate validations from AR objects which plays well with the idea of form objects and splitting AR into Domain Objects and persistence layer.
DHH encourages us for more intensive usage of Concerns, which he compares to DCI idea. Actually, it’s not the same, so keep in mind Victor Savkin’s page about Ruby and DCI. If we are talking about DCI—there are some interesting voices in the discussion. Gregory Brown raised some common issues about this paradigm, and Rebo responsed with nice article about DCI’s ‘C’ part.
We are looking forward for another possible cure of AR-madness, which could be DataMapper 2. Solnic just wrote some notes about plans and roadmap of this well looking project. You also try some already available patterns to decompose fat AR models.
‘Small’ patterns are as important as those ‘big’ ones. So don’t forget that booleans are bad and do not overuse subject in rspec tests.
Hackers toolbox
During last month we found some cool stuff and tips which could improve your hackers toolbox. Vidir is a vim-like tool for mass-edit of file names—open directory, edit names with power of Vim, save and quit and voilà! If you like functional programming you will probably add this simple map alias into your shell. For impatient devs we’ve got another tip, that would automatically reload your irb session.
From our stable: Tymon has a nice tip about tmux and ssh and Adam reveals hidden gems in less.
That’s all for today. Thanks for reading and I wish you a Happy New Year!
Backbone.js, Rails 3 and asynchronous interfaces
Currently at Monterail we’re building a frontend-heavy app. With a lot of cool stuff. Like preloading data for daily views so that you won’t have to wait for every surrounding day to load. Many, many interactive elements that should be really interactive – not in an oldschool, ajax “spinner” way. And a lot of other stuff we shouldn’t talk about yet.
We decided to put more emphasis on Backbone.js with Rails and build most of the app with this combination.
Below you can find some of our findings on the topic of making those two speak to each other in a nice manner.
Asset pipeline
Really makes things easier. Organizing your JS code before it meant a lot of things: handling dependencies, minifying & compressing the code, organizing it into a lot of different files and building connections between them. With asset pipeline and it’s awesome “require” directive, you can forget about handling it manually. You just create the structure in app/assets/javascripts. Or app/assets/javascripts/backbone. Your choice. You can have many different files in whatever directories you like – in the end you’ll get one (or more, if you decide to) JS file that has everything your webapp need.
Don’t forget to:
- gzip your JS/css assets
- minify them!
Rails-backbone gem
Also makes things easier. It puts backbone.js (& underscore.js) library code into your asset pipeline. It gives you some great generators to get started if you don’t know where to start. Definitely worth including in your Gemfile.
http://rubygems.org/gems/rails-backbone
Coffeescript & underscore.js
Obviously. It makes Javascript code feel fresh. You can fold, iterate and do a lot of stuff. Be sure to check out underscore.js docs. And coffescript docs, of course, but I bet you know it already.
“V” in MVC done right
Views are classes. You handle logic inside them using the hardcore code, which is receiving so much hate in Rails views. They can have many methods, they do not have to render a single “action”. And templates are templates – and nothing else. There is a lot of discussion recently on the matter of Views/Templates in Rails, but you can start implementing it right away. Go and try it. You’ll fall in love.
You have many options for templates – eco, Handlebars.js, {{ mustache }}, but we recommend haml_coffee_assets. It gives you the HAML you (probably) already know with the ability to use coffeescript within it.
Single-page app
Yes. You will use Rails to render only the core view of the application. Of course, you will have to handle all things around the core of your app.
For example, you should probably leave out:
- authentication logic: sign up / in, reset password
- steps necessary to use the app: billing pages
- if they do not include any dynamic data, they are lightning fast
- if they include dynamic data, you can preload it and they still will be lightning fast
Asynchronous interface
Since you’ll be doing most of the association/validation work in background, you don’t have to worry too much about server responses. It’s the same with rendering elements: you’ll be doing it in JS, not Rails. You don’t have to wait for the request to return the rendered element to actually display them. If you create something, the UI should respond right away and the ajax request can persist the data with it’s own pace.
You should, however, prepare a global “something went wrong, reload the page” request rescuer – in case of network outages, backend app failures and so on. But it’s way easier to manage.
API
You get it for free. It doesn’t have to be public, but if you decide to, it’s a matter of adding OAuth as the way of authenticating requests and it’s done. (Or you could go with http basic auth, which is obviously bad for a lot of reasons).
With smart caching (give redis-store a try), you’ll save tons of server power. You know, relating to the old ruby saying, “views are most resource-consuming”.
Conclusion
There are, obviously, downsides, which I’ll cover in future (I silently hope that there won’t be any reason to do that). And topics I haven’t covered. I’m curious about other stacks and ways of doing things. Share in the comments or ping me on Twitter!
Testing AJAX-reloaded elements with Capybara
Capybara is a great tool for testing application flow and user interfaces. Thanks to Culerity and Selenium web drivers you can test javascript and AJAX features of your apps.
However testing AJAX is not that straightforward because of asynchonous nature of these requests. In order to test effects of AJAX calls a web driver must wait until they finish which is difficult if not impossible to detect. However this can be worked around using some tricks.
Scenario 1
Let’s say you want to test a link on the page that triggers an AJAX request which eventually inserts some element in DOM. This could be an “Edit” link that retrieves object’s data from database (via AJAX call) and then shows an HTML form on the page. Test scenario might look like this:
@selenium Scenario: Clicking "Edit" should show edit form Given I am on product's page And I click "Edit" link Then I should see form
The form will not appear immediately after the link is clicked because it takes some time for your app to process the AJAX request and return the response. Capybara is intelligent enough to acknowledge this and instead of expecting a form to appear immediately it periodically looks for it in page’s DOM. You can define for how many seconds it should keep looking by setting following option:
Capybara.default_wait_time = 5
If the form doesn’t appear in this specified time frame, the test fails. Note that Capybara doesn’t always wait full 5 seconds, it simply moves on to the next step as soon as the form appears.
Scenario 2
Now you want to test a link that removes an element from DOM. This could be a “Save” link that saves object to database (via AJAX call) and then removes the form. Test scenario:
@selenium Scenario: Clicking "Save" should remove edit form Given I am on product's page And I click "Save" Then I should not see form
This test fails even if your app works as expected. I guess you see the problem. Capybara finds the form on first lookup which is performed immediately after the click, but at that point the AJAX call has not completed yet, so the form is still there.
Popular solution is to explicitely tell Capybara to wait until it starts looking for changes in DOM. In other words, give the AJAX calls chance to complete. Here’s an adequate cucumber step:
Given /^I wait (\d+) seconds?$/ do |sec| sleep(sec.to_i) end
Rebuilt scenario would look like this:
@selenium Scenario: Clicking "Save" should remove edit form Given I am on product's page And I click "Save" And I wait 5 seconds Then I should not see form
The downside is that it will always wait 5 full seconds now.
Other aspects
Neither explicit waiting with sleep, nor Capybara’s default_wait_time option guarantees 100% success. Your tests may still ocassionally fail if the AJAX requests they perform takes longer than you assume. The time it takes for the app to process such request may be quite random as it depends on many aspects like machine load or external services it hits (database, search engines, etc.). So remember to set the option to a value high enough for your app.
Fixtures without validation with Factory Girl
Factory Girl is my fixture replacement library of choice. It improves tests readability and maintainability. It’s also customizable.
There are sometimes situations when you want to create test scenarios that checks how your app is handling invalid data (not user input, but invalid records that already sit in your database). To do this you first need to put this invalid data to your db.
You could accomplish this with such line:
@user = Factory(:user, :email => "not a correct email address")
However, factory girl would raise an exception here, because that’s the default strategy of creating new fixtures – raise exception if save fails (because of validation errors for example).
Thankfully we can use our own strategy of creating new fixtures, such that does save records without validation.
First let’s define our new strategy:
class Factory::Proxy::CreateWithoutValidation < Factory::Proxy::Build
def result
@instance.save(false)
@instance
end
end
class Factory
def self.create_without_validation (name, overrides = {})
factory_by_name(name).run(Proxy::CreateWithoutValidation, overrides)
end
end
Now we can use it while defining new factory:
Factory.define :invalid_user, :class => User, :default_strategy => :create_without_validation do |f|
...
end
And then we can happily create invalid fixtures without any exceptions raised.
Turning off auto timestamping for testing in Rails
Suppose that you implemented a functionality that depends on values of created_at or updated_at fields of your models. How do you test it?
If you use fixtures that reside in test/fixture/*.yml files then there is no problem, because the values you set there for created_at and updated_at fields are saved to the database ‘as is’. So you can easily have an article created one week ago:
article:
title: What a great day
created_at:
updated_at:
However, I don’t use fixtures files myself. I feel a bit dirty using them ;) I find fixture replacement tools far more maintainable. Namely, I love thoughtbot’s Factory Girl. But here comes the problem. This won’t work as expected with Factory Girl:
Factory(:article, :created_at => 1.week.ago, :updated_at => 1.week.ago)
That’s because ActiveRecord’s automatic timestamping feature sets Time.now for created_at and updated_at fields overriding our values. At least that’s ActiveRecord’s default behavior. Fortunately it can be disabled with:
Article.record_timestamps = false
Chances are that after creating a model with a custom timestamp we’ll want to turn automatic timestamping back on. But turning it off and on in many places in your unit tests would be pretty cumbersome. Wouldn’t it be cool if you could achieve all of this with a snippet below?
without_timestamping_of Article do
Factory(:article, :created_at => 1.week.ago, :updated_at => 1.week.ago)
end
It turns timestamping off, executes the block and turns timestamping back on. I find it clean and dry. Here’s the code to place in your test_helper.rb:
# test_helper.rb
class Test::Unit::TestCase # or class ActiveSupport::TestCase in Rails 2.3.x
def without_timestamping_of(*klasses)
if block_given?
klasses.delete_if { |klass| !klass.record_timestamps }
klasses.each { |klass| klass.record_timestamps = false }
begin
yield
ensure
klasses.each { |klass| klass.record_timestamps = true }
end
end
end
end
Of course you can turn off timestamping for many models at once:
without_timestamping_of Article, Comment, User do
Factory(:article, :created_at => 1.week.ago, :updated_at => 1.week.ago)
Factory(:comment, :created_at => 1.day.ago)
Factory(:user, :updated_at => 5.hours.ago)
end
Hope you like it. If so, share :)
Dynamic cookie domains with Rack’s middleware
Handling sessions in multi-domain environment is not the simplest things to do, because of the fact that cookies are scoped to a domain they were set by.
Recently we were developing an application with such an idea in mind:
- Application will work as a base for other mini-applications (which we call sites)
- Each site can be accessed via different url types: site.example.org and example.org/site
- We want the users to remain logged in when switching from one url type to another
I won’t be covering application structure, routing, etc. here, I will only write about maintaing the sessions is such an environment.
So this is pretty simple here – all that we needed to do was to set cookie domain to .example.org (note the “dot” at the beginning). This could be done via:
ActionController::Base.session = {
:domain => ".example.org"
}
However there was an additional requirement that we need to deal with:
- Each site can be accessed via custom domain – site.com
- Of course there’s no way here to keep the user logged in when he’s switching from site.com to example.org/site or site.example.org, at least it cannot be done with setting cookie domain to whatever value
Technically, to access the site via site.com, that domain must point to our IP address. Then we need to detect that the site is being accessed via custom domain and set cookie domain respectively.
This could be done via some funky before_filters in an Application Controller, however we found much better and cleaner way.
Rack’s middleware to the rescue
Rack itself is a minimal interface between web server and your ruby framework. It’s used by Ruby on Rails (since 2.3) and Merb. The request comes from web server, goes through middleware layers and enters the application.
So we wrote a middleware layer that detects the host with which our application is accessed and sets cookie domain for the request. Here it is:
app/middlewares/set_cookie_domain.rb
class SetCookieDomain
def initialize(app, default_domain)
@app = app
@default_domain = default_domain
end
def call(env)
host = env["HTTP_HOST"].split(':').first
env["rack.session.options"][:domain] = custom_domain?(host) ? ".#{host}" : "#{@default_domain}"
@app.call(env)
end
def custom_domain?(host)
domain = @default_domain.sub(/^\./, '')
host !~ Regexp.new("#{domain}$", Regexp::IGNORECASE)
end
end
Now we need to turn it on:
environment.rb
config.load_paths += %W( #{RAILS_ROOT}/app/middlewares )
production.rb
config.middleware.use "SetCookieDomain", ".example.org"
.example.org is the default domain that will be used unless the application is accessed via custom domain (like site.com), we give it different values depending on environment (production/staging/development etc).
And since we’re fans of test driven development, here’s the test that ensures us that everything works as expected:
tests/integration/set_cookie_domain_test.rb
require 'test_helper'
class SetCookieDomainTest < ActionController::IntegrationTest
context "when accessing site at example.org" do
setup do
host! 'example.org'
visit '/'
end
should "set cookie_domain to .example.org" do
assert_equal '.example.org', @integration_session.controller.request.session_options[:domain]
end
end
context "when accessing site at site.com" do
setup do
host! 'site.com'
visit '/'
end
should "set cookie_domain to .site.com" do
assert_equal '.site.com', @integration_session.controller.request.session_options[:domain]
end
end
context "when accessing site at site.example.org" do
setup do
host! 'site.example.org'
visit '/'
end
should "set cookie_domain to .example.org" do
assert_equal '.example.org', @integration_session.controller.request.session_options[:domain]
end
end
end
Test is sponsored by great Shoulda and Webrat gems.
Feel free to comment and share.
Rails 2.2.2, Ajax and respond_to
As I wrote some time ago in the article about Rails, Ajax and jQuery, sometimes there are problems with Rails not interpreting correctly content type headers of ajax requests. It’s because not all web browsers send that header in the same way.
What I proposed was to sort the request.accepts array (array containing content type headers sent by browser) so that xml content type would be the first element. That would then trigger format.xml in our respond_to block.
However that approach does not work in Rails 2.2.2, because now the request.accepts array is frozen and it cannot be modified. I spent some time googling for the solution, but with no effect. So I dived into the API and Rails’ source code and came up with pretty nice and simple solution to the problem.
class ApplicationController < ActionController::Base
before_filter :xhr_to_xml
def xhr_to_xml
request.format = :xml if request.xhr?
end
end
This piece of code is an equivalent of the snippet I proposed in the article I referred to at the beginning. Now all ajax request will trigger format.xml in respond_to blocks.
Materials from AIESEC Conference in Częstochowa, Poland
Last month the netguru team has been invited to Corporate Responsibility and Information Technology Conference organized by AIESEC (international student organization) which took place in Częstochowa, Poland. We went there and gave some talks on various topics and I think some of you may find materials from this conference quite interesting.
First, we led an unofficial discussion on “The (fading) differences between desktop and web applications”. We introduced the topic to our hearers and then let them express themselves, it was quite successful.
On Day 2, we organized Ruby on Rails workshops which consisted of “Introduction to RoR” talk and one-hour live-coding session during which a simple twitter like application has been developed. You can find the results on our Github account.
We finished Day 2 with a talk on Enterprise 2.0 and on Day 3 we talked about “Developing a web-development company (startup)”, which was quite different from what people heard on earlier talks gave by the representatives of big corporations. I have no materials to share on this, though. Sorry.