Rails 3 was released this week but the minds of the Rails Core team members are already focused on the 3.1 release for quite some time. DHH was the first one to give a hint on what we would like to see in Rails 3.1 in his RailsConf talk and, as Ruby Summer of Code is close to its end, we are able to see the work of several students getting solid enough to be an important part of Rails 3.1 release.
In between all this work, I was invited to participate in three important conferences in the following months and lately I’ve prepared enough material to give a talk entitled “Rails 2.3, 3.0 and 3.1: Past, Present and Future“!
In this talk I plan to discuss many of the conceptual changes done in Rails 3 and how these changes were given life in the Rails source code, comparing, as much as possible, with Rails 2.3. After the current and past scenarios are throughly discussed, I will show how much of the work done in Rails 3 can still be improved and how several Ruby Summer of Code Projects are helping us to achieve it. And if you ever wondered how much Merb affected the Rails community, you will have a few surprises while watching this talk!
The three different conferences I mentioned above will be held in Ukraine, Brasil and Sweden. But I’m not sure if there will be anyone recording them, so I’d suggest you not to miss any of them.
Here they are…
1) RubyConfUA (Ukraine): 16th and 17th October
If you have never been to Kyiv before (just like me), here’s a great opportunity to visit it for the first time! It will be two days of deep immersion into Ruby with nice city visits during the night!
Other active developers in the community as Oleg Andreev and Piotr Sarnacki will be present as well. By the way, RubyConf Ukraine is still accepting both sponsors and talk proposals! We are waiting for you!
2) RubyConf (Brasil): 26th and 27th October
Right after RubyConf Ukraine, I’ll be flying back to Brasil to present a portuguese version of this talk. RubyConf Brasil is the former “Rails Summit Latin America” (which has been the largest Ruby and Rails conference in Latin America for the last two years). And this year it won’t be different. Several Ruby and Rails developers (like Yehuda Katz, Charles Nutter, Evan Phoenix and many others) have confirmed their presence. If you get the chance, don’t miss it!
3) Oredev (Sweden): 8th to 12th November
And finally, my last stop will be in Sweden at the developer conference held in Malmö. This will be different from the previous two, since it is not focused in Ruby nor Rails. It’s a multitrack conference that hosts different technologies (by the way, the keynotes and tutorials programme is very interesting!). Since I’m expecting several non-Rails developers in the audience, I will slightly change my talk to focus more on the conceptual side and less on technical discussions. Also, I’ll be hosting a a workshop about Rails 3, where I’ll demonstrate a few of @plataformatec’s open source projects.
If you are coming to any of these events, please let me know in the comments!
Tags: conference, events, rails 3.1, rubyconf
Posted in English | View Comments
De ‘Rails 3′ a ‘Project Rescue’: 5 palestras sobre Rails que valem a pena dar uma checada!
By José ValimEm agosto, a Plataforma Tecnologia esteve presente de Norte ao Sul do Brasil. Nada mais, nada menos que 7.000 km foram percorridos, cinco palestras, quatro eventos e menos horas de sono de todo nosso time. Mas….. valeram cada segundo e centavo investidos!
Esse post é um agregado de todas essas palestras com os respectivos slides (os vídeos serão adicionados assim que eles tornarem disponíveis). Então vamos lá!
Oxente Rails – 05 e 06 de Agosto (Natal)
O Oxente Rails foi espetacular! Não há dúvidas de que valeu o esforço que fizemos para levar boa parte do nosso time (Carlos Antônio, Fabio Yamate, Hugo Baraúna, eu (José Valim) e Vinícius Baggio) e em patrocinar o evento. Aproveitamos o espaço para agradecer o pessoal da Tink! por nos convidar para dar duas palestras e pela organização do evento, que foi impecável. Nos vemos novamente no ano que vem!
Bem, a primeira apresentação foi a minha e falei sobre algumas das ferramentas Open Source da Plataforma Tec (Devise, Simple Form e Responders):
Logo em seguida, o Hugo Baraúna falou sobre Project Rescue (um dos nossos serviços) e contou como fazemos para salvar projetos à beira de precipícios. Veja nos slides abaixo.
Além de um excelente evento, a cidade de Natal também reserva diversas surpresas. Um dia após o evento, nós e um pessoal da comunidade Rails fomos visitar as dunas e nos divertimos bastante. Valeu NATAL!
WhyDay – 19 de Agosto (São Paulo)
Após retornarmos de Natal, fui convidado a falar um pouco sobre Open Source e contribuições no Rails em comemoração ao WhyDay. Seguem os slides.
Update (feito no dia 30/ago): a InfoQ publicou o vídeo da apresentação.
A palestra foi bastante descontraída e logo após nós hackeamos um pouco:
Valeu @rafaelrosafu pelo convite!
RS on Rails – 21 de Agosto (Porto Alegre)
Mas enquanto nós nos divertiamos no WhyDay aqui em São Paulo, o Carlos Antônio estava terminando a sua palestra para o RS on Rails. A palestra contemplou diversas novidades do Rails 3 como o Bundler, ARel, ActiveModel, entre outras.
Na foto abaixo, parece que o Carlos é político em campanha eleitoral. Mas na verdade ele só está dizendo que o Router é 100% compatível com Rack. Confira toda apresentação nos slides.
Valeu @felipebcabral pela organização do RS on Rails e pelo convite! Ano que vem queremos mais! =D
The Developers Conference – 21 de Agosto (São Paulo)
Enfim, após poucas horas da apresentação do Carlos Antônio em Porto Alegre, lá estava eu apresentando outra palestra sobre Rails 3 no The Developers Conference. O interessante é que as duas apresentações se complementam. No TDC, falei sobre generators e a API de notificações, enquanto que no RS on Rails o Carlos Antônio falou sobre Unobtrusive Javascript e XSS Protection. Vale a pena conferir as duas apresentações.
E mais uma vez, obrigado ao @rafaelrosafu e ao @felipero pelo convite.
Encerrando
Agosto foi um mês bem intenso, repleto de eventos bem organizados e com conteúdo muito interessante. Tanto é verdade que nós não conseguimos participar de todos eventos que gostaríamos. Mas posso dizer que estamos contentes em ver como a comunidade Ruby e Rails está crescendo de forma bastante saudável aqui no Brasil. Parabéns a todos que estavam presentes nestes eventos, como espectadores ou organizadores. Valeu comunidade Rails!
Nos vemos em Outubro no RubyConf! E não esqueçam de conferir outras palestras da Plataforma Tec no Slideshare.
Posted in Português | View Comments
A couple weeks ago we finally released Devise 1.1 which is fully-compatible with Rails 3! Not only that, we’ve been working with Rails 3 since the first betas and several features were added along the way! Let’s take a look at those, some architectural changes and see how Devise 1.1 and Rails 3 will change how you handle authentication.
Pretty URLs with Metal
A common complaint in Devise 1.0 (for Rails 2.3) was, in order to know which message to show to the user when sign in failed, we had to pass a parameter in the URL as in /users/sign_in?unauthenticated=true while one would expect us to simply use flash messages. This happened because the redirection was done not from inside a controller, but a Rack application set up in Warden (a Rack authentication framework Devise relies on) and we could not access flash messages from it.
However, since Rails 3 moved several responsibilities to the Rack layer, including flash messages, we can easily access flash messages from any Rack application, allowing us to remove the parameter from the URL! Even more, Rails 3 provides small, fast, bare bone controllers through ActionController::Metal, which we used in Devise to clean and speed up the code considerably.
Locking goodness
The lockable module in Devise also went through a major overhaul. Previously, it already supported :unlock_strategy as option, allowing you to specify if the user could be automatically unlocked after a time period, through an e-mail token or both. Now, it also supports :none as option, meaning that all unlocking should be done manually.
Even more, there is a new option called :lock_strategy, that allows you to specify whether the lock happens only manually or after an amount of invalid sign in attempts.
HTTP Authentication on by default
In Devise 2.3, you may remember that we had a module called :http_authenticable along with :database_authenticatable and :token_authenticatable. While all three worked great, it was confusing that all HTTP authentication features were built on top of the database authentication and it was not possible to do HTTP authentication using a token unless we created a forth module called :http_token_authenticatable. We quickly noticed this could be improved by providing a better design and better abstract Devise authentication strategies.
And that is what happened in Devise 1.1. Now both database and token authentication work through HTTP with no extra work and the http authenticatable module was deprecated. Besides, if you are creating a new strategy on your own, you get both authentication through parameters (form) and HTTP with no extra work!
Routing customizations
We built Devise to be a full stack solution with customization in mind. In Devise 1.1, the customization abilities from Devise were taken to the next level. Now the devise_for method in routes accepts to extra options: :skip and :controllers. The first one allows you to skip the routes generation for a given controller/module in case you want to define them on your own, while the second allows you to change the router to point to a given controller in your application, like Users::ConfirmationsController instead of Devise’s internal controller.
Talking about Devise’s internal controller, Devise 1.1 namespaced all controllers classes, so now we have Devise::ConfirmationsController instead of ConfirmationsController.
Another limitation removed from Devise in this new version is related to URLs customizations. In prior versions, Devise used the URL to retrieve which scope is being accessed. That said, if you were accessing “/users/sign_in”, Devise had to inspect this URL and find the “/users” bit to specify the current scope is “users”. The same happened to “/admin/sign_in”.
This had a huge impact in URL customization, because if you wanted to have an URL like “/some_prefix/users/sign_in”, you had to tell Devise you were appending a prefix. Things could get even uglier if you wanted to prepend dynamic prefixes like “/:locale”.
In Devise 1.1, we use the new contraints API and Rack capabilities from the new router to specify which scope to use. So, instead of inspecting the URL, Devise retrieves the user from the request’s env hash as request.env["devise.mapping"].
For all the routes generated by devise_for, Devise automatically sets this value in the env hash. However, if you are creating your own routes, you need to set it manually using the constraints API:
constraints lambda { |r| r.env["devise.mapping"] = Devise.mappings[:user] } do # Add a custom sign in route for user sign in get "/sign_in", :to => "devise/sessions" end
Of course, since this is rather a common pattern, we encapsulated it in a nice API:
devise_scope :user do # Add a custom sign in route for user sign in get "/sign_in", :to => "devise/sessions" end
You can simply give a block to devise_for as well and get the same result:
devise_for :users do # Add a custom sign in route for user sign in get "/sign_in", :to => "devise/sessions" end
All the routes specified in the block have higher priority than the ones generated by devise_for.
Awesomeness pack
The last feature we want to discuss is also a routing customization, but we decided to leave it up for last because it shows all the potential coming with Rails 3 and Devise 1.1.
In Devise 1.1, we added the ability to require authentication for a given url in the router, besides the existing before filters in controllers. This allow us to easily require authentication for third party rack application without a need to hack into them. Kisko Labs posted an interesting case where you can use Devise to require authentication to a Resque application in very few lines of code:
authenticate :admin do mount Resque::Server.new, :at => "/resque" end
Devise simply uses the constraints API discussed above, allowing the request to continue only if the user is already authenticated. Otherwise, it redirects the admin to the sign page managed by Devise inside your Rails application. Indeed, when you have Rack, Rails 3 and Devise 1.1 playing along, great things can be accomplished quite easily!
There are several other features, bug fixes and deprecations included in this release, we invite you to check the CHANGELOG and take a look at them!
And we are happy to say this is not all, there is much more to come in Devise 1.2, including OAuth2 support which is already added in the master branch. Enjoy!
Tags: authentication, devise, rails 3
Posted in English | View Comments
You probably know that Active Record got a facelift and is now powered by Active Relation. A new chainable-award-winning-lazy API was added and received great feedback! However, as more and more people are trying Rails 3 beta, a small incompatibility between the old and new syntax was found. This post explains this incompatibility and how it was solved.
The issue
Quoting the Lighthouse ticket, imagine the following scenario in Rails 2.3:
class Page < ActiveRecord::Base default_scope :conditions => { :deleted_at => nil } def self.deleted with_exclusive_scope :find => { :conditions => "pages.deleted_at IS NOT NULL" } do all end end end
If you rewrite it to the new 3.0 syntax, your first attempt would probably be:
class Page < ActiveRecord::Base default_scope where(:deleted_at => nil) def self.deleted with_exclusive_scope :find => where('pages.deleted_at IS NOT NULL') do all end end end
However, if you try it out on console, you will find out it does not work as expected:
Page.all #=> SELECT "pages".* FROM "pages" WHERE ("pages"."deleted_at" IS NULL) Page.deleted.all #=> SELECT "pages".* FROM "pages" WHERE ("pages"."deleted_at" IS NULL) AND ("pages"."deleted_at" IS NOT NULL)
To understand why it does not work, let’s take a look at the source code!
Investigating the issue
With Active Relation, Active Record is no longer responsible to build queries. That said, ActiveRecord::Base is not the one that implements where() and friends, in fact, it simply delegates to an ActiveRecord::Relation object. From ActiveRecord::Base source code:
delegate :select, :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped
And the scoped implementation is shown below:
def scoped(options = nil) if options.present? scoped.apply_finder_options(options) else current_scoped_methods ? relation.merge(current_scoped_methods) : relation.clone end end def relation @relation ||= ActiveRecord::Relation.new(self, arel_table) finder_needs_type_condition? ? @relation.where(type_condition) : @relation end
As you can see, scoped always returns an ActiveRecord::Relation that you build your query on top of (notice that ARel::Relation is not the same as ActiveRecord::Relation).
Besides, if there is any current_scoped_methods, the scoped method is responsible to merge this current scope into the raw relation. This is where things get interesting.
When you create your model, current_scoped_methods returns by default nil. However, when you define a default_scope, the current scope now becomes the relation given to default_scope, meaning that, every time you call scoped, it returns the raw relation merged with your default scope.
The whole idea of with_exclusive_scope is to be able to make a query without taking the default scope into account, just the relation you give in as argument. That said, it basically sets the current_scope_methods back to nil, so every time you call scoped to build your queries, it will be built on top of the raw relation without the default scope.
With that in mind, if we look again at the code which we were trying to port from Rails 2.3, we can finally understand what was happening:
def self.deleted with_exclusive_scope :find => where('pages.deleted_at IS NOT NULL') do self end end
When we called where('pages.deleted_at IS NOT NULL') above, we were doing the same as: scoped.where('pages.deleted_at IS NOT NULL'). But, as scoped was called outside the with_exclusive_scope block, it means that the relation given as argument to :find was built on top of default_scope explaining the query we saw as results.
For example, the following syntax would work as expected:
def self.deleted with_exclusive_scope do where('pages.deleted_at IS NOT NULL').all end end
Since we are calling where inside the block, the scoped method no longer takes the default scope into account. However, moving the relation inside the block is not the same as specifying it to :find, because if we were doing three queries inside the block, we would have to specify the same relation three times (or refactor the whole code to always do a query on top of this new relation).
That said, it seems the previous with_exclusive_scope syntax does not suit very well with ActiveRecord’s new API. Maybe is it time for change? Can we provide a better API? Which are the use cases?
Identifying the use cases
The with_exclusive_scope method has mainly two use cases. The first one, which we just discussed above, is to allow us to make a query without taking the default scope into account inside our models:
def self.deleted with_exclusive_scope do where('pages.deleted_at IS NOT NULL').all end end
While this code looks ok, if we think about relations, we will realize that we don’t need to give a block to achieve the behavior we want. If the scoped method returns a raw relation with the default scope, couldn’t we have a method that always returns the raw relation? Allowing us to build our query without taking the default scope into account?
In fact, this method was already implemented in Active Record and it is called unscoped. That said, the code above could simply be rewritten as:
def self.deleted unscoped.where('pages.deleted_at IS NOT NULL').all end
Much simpler! So, it seems that we don’t need to support the block usage at all, confirm?
Deny! Going back to the Page example above, it seems we should never see deleted pages, that’s why we set the default_scope to :deleted_at => nil. However, if this application has an admin section, the admin may want to see all pages, including the deleted ones.
That said, what we could do is to have one controller for the normal User and another for the Admin. In the former, we would always use Page.all, and Page.unscoped.all in the latter.
However, if these controllers and views are very similar, you may not want do duplicate everything. Maybe it would be easier if we do something like this:
def resource_class if current_user.is_admin? Page.unscoped else Page end end
And, instead of always referencing the Page class directly in our actions, we could call resource_class. While this solution is also ok, there is a final alternative, that would require no changes to the current code. If you want to use the same controller for different roles, but changing the scope of what they are allowed to see, you could simply use an around_filter to change the model scope during the execution of an action. Here is an example:
class PagesController < ApplicationController around_filter :apply_scope # some code ... protected def apply_scope if current_user.admin? Page.with_exclusive_scope { yield } else yield end end end
That said, being allowed to give a block to with_exclusive_scope is actually useful and since we want to deprecate with_exclusive_scope in favor of unscoped in the future, we brought this very same syntax to unscoped as well:
def apply_scope if current_user.admin? Page.unscoped { yield } else yield end end
Tidying it up
Well, after the behavior in with_exclusive_scope was properly ported to the new API, we need to be sure we are not forgetting about anything… wait, actually we are.
with_exclusive_scope has an evil twin brother called with_scope which behaves very similarly, except that it always build the query on top of the scoped relation. It works like this:
class Page < ActiveRecord::Base default_scope where(:deleted_at => nil) end Page.with_scope :find => { :conditions => { :active => true } } do Page.all #=> Bring all active pages that were not deleted end
However, this feels way too hash-ish. Of course, we could use relations to make it a bit prettier:
Page.with_scope :find => where(:active => true) do Page.all #=> Bring all active pages that were not deleted end
This is ok, but it seems that we could improve it even more. That said, we added a new method to relations, called scoping:
Page.where(:active => true).scoping do Page.all #=> Bring all active pages that were not deleted end
Yeah! Sign me up ’cause this looks way better than the previous syntax! And, if you check the original commit, you will notice the unscoped method with a block simply delegates scoping:
def unscoped block_given? ? relation.scoping { yield } : relation end
So, with unscoped and scoping implemented, we just need to commit, git push and be happy, confirm? Deny! There is one last case to check.
create_with
If you payed attention properly, you can notice that every time we called with_exclusive_scope and with_scope, we always passed { :find => relation } as hash, instead of simply giving the relation. This happens because these methods accept two hash keys: find and create.
As you may expect, one specifies the behavior for create and the other for finding. In most of the cases, they are exactly the same and work with the new syntax:
page = Page.where(:active => true).new page.active #=> true
However, for obvious reasons, this only works if the conditions are given as a hash. Consider this case:
page = Page.where("active = true").new page.active #=> nil
That said, there may be a few scenarios where you want to specify the creation conditions on its own, explaining the :find and :create options in with_exclusive_scope and with_scope methods. So, how can I achieve it with the new syntax? Easy!
page = Page.create_with(:active => true).new page.active #=> true
If you provide both conditions as a hash and create_with, create_with always have higher priority:
page = Page.where(:active => false).create_with(:active => true).new page.active #=> true
Note this syntax already existed, we are just making it explicit now as part of the new API! That said, commit, push and be happy!
Wrapping up
All in all, with_exclusive_scope and with_scope are now part of the old ActiveRecord API giving place to the new, strong and vibrant unscoped and scoping methods!
However, they are not going to be deprecated now. They will follow the same deprecation strategy as all the current methods.
And you? What do you think about this new scoping API?
Tags: activerecord, arel, rails3, scopes
Posted in English | View Comments
At the end of May, I was honored to talk at Euruko, the most important Ruby event in Europe! The event was excellent, the organizers made an awesome work before, during and they are still keeping it up by releasing several videos along this week!
Without further ado, here follows my talk video:
DSL or NoDSL by José Valim from Krakow Tech Conferences.
Also, I uploaded my slides (even though they do not make a lot of sense by themselves) as a way to complete the awesome list of resources being wrapped at http://euruko2010.heroku.com/! I’m definitely looking forward to Euruko 2011 in Berlin!
Posted in English | View Comments
One of the beauties in the Open Source world is the possibility of reading other people source code and learn new things. However, lately I found out that not only the library code, but the test suite of several open source projects are full lessons for us.
In this post, I want to tell you which are the three test suites that I admire the most and why.
Integration award: Railties
Rails 3 has several improvements and not all of them may be visible to the application developer. One of the hidden unicorns is Railties test suite. As Yehuda stated in a blog post during the refactoring of version 2.3 to 3.0:
“Although the Rails initializer tests covered a fair amount of area, successfully getting the tests to pass did not guarantee that Rails booted.”
This happened because, in order to have fast tests, Rails 2.3 suite stubbed and mocked a significant part of the booting process. The new test suite is able to create a new application using the application generator, change configuration options, add plugins and engines, boot it and even make HTTP requests using Rack::Test.
For instance, take a look at this test which ensures that app/metals inside plugins are successfully added to the application middleware stack:
def test_plugin_metals_added_to_middleware_stack @plugin.write 'app/metal/foo_metal.rb', <<-RUBY class FooMetal def self.call(env) [200, { "Content-Type" => "text/html"}, ["FooMetal"]] end end RUBY boot_rails require 'rack/test' extend Rack::Test::Methods get "/not/slash" assert_equal 200, last_response.status assert_equal "FooMetal", last_response.body end
The most important lesson here is: whenever mocking or stubbing in our tests, we still need to add tests without the mocks and stubs to ensure all API contracts are respected.
Readability award: Capybara
Capybara is a tool to aid writing acceptance tests for web applications. Capybara can use several drivers to interact with a web application, as Selenium, Celerity or even Rack::Test. Each driver needs a different setup and has different features. For instance, both Selenium and Celerity can handle javascript, but not Rack::Test.
As you may imagine, all these different drivers can make a test suite become a real spaghetti. However, Jonas Nicklas was able to transform a potential problem into a very elegant and readable test suite with Rspec help. Here is, for instance, the tests for selenium:
describe Capybara::Driver::Selenium do before do @driver = Capybara::Driver::Selenium.new(TestApp) end it_should_behave_like "driver" it_should_behave_like "driver with javascript support" end
Each behavior group above (“driver” and “driver with javascript support”) is inside Capybara library allowing everyone to develop its own extensions using a shared suite. For instance, if a driver has javascript support, it means the following tests should pass:
shared_examples_for "driver with javascript support" do before { @driver.visit('/with_js') } describe '#find' do it "should find dynamically changed nodes" do @driver.find('//p').first.text.should == 'I changed it' end end describe '#drag_to' do it "should drag and drop an object" do draggable = @driver.find('//div[@id="drag"]').first droppable = @driver.find('//div[@id="drop"]').first draggable.drag_to(droppable) @driver.find('//div[contains(., "Dropped!")]').should_not be_nil end end describe "#evaluate_script" do it "should return the value of the executed script" do @driver.evaluate_script('1+1').should == 2 end end end
Capybara test suite is one of the best examples of using tests as documentation. By skimming the test suite you can easily know which features are supported by each driver! Sweet, isn’t it?
Friendliness award: I18n
When you are a big Open Source project, your test suite needs to be easy to run in order to new developers can create patches without hassle. The I18n library for Ruby definitely meets the big Open Source project requirement since it’s widely used and provides several extensions.
However, some of these extensions depends on ActiveRecord, some in ruby2ruby, others in ruby-cldr… and soon it will even support a few Key-Value stores, as Tokyo and Redis. Due to all these dependencies, you would probably imagine that running I18n test suite would require several trials and a lot of configuration before it finally works, right?
WRONG! If you don’t have ActiveRecord, I18n will say: “hey, you don’t have ActiveRecord” but still run the part of test suite that does not depend on it. So if a developer wants to fix or add something trivial, he doesn’t need to worry with installing all sorts of dependencies.
Besides, as mentioned a couple months ago, the I18n library allows you to create several combinations of backends. In other words, the I18n test suite needs to ensure that all these different combinations work as expected.
This problem is quite similar to the one in Capybara which needs to test different drivers. However, I18n uses Test::Unit thus it cannot use shared examples groups as in Rspec. So how were I18n developers able to solve this issue? Using Ruby modules!
Here are the tests for the upcoming KeyValue backend:
require 'test_helper' require 'api' class I18nKeyValueApiTest < Test::Unit::TestCase include Tests::Api::Basics include Tests::Api::Defaults include Tests::Api::Interpolation include Tests::Api::Link include Tests::Api::Lookup include Tests::Api::Pluralization # include Tests::Api::Procs include Tests::Api::Localization::Date include Tests::Api::Localization::DateTime include Tests::Api::Localization::Time # include Tests::Api::Localization::Procs STORE = Rufus::Tokyo::Cabinet.new('*') def setup I18n.backend = I18n::Backend::KeyValue.new(STORE) super end test "make sure we use the KeyValue backend" do assert_equal I18n::Backend::KeyValue, I18n.backend.class end end
Each included module above adds a series of tests to the backend. Since key-value backends cannot store procs, we don’t include any test related to procs.
Wrapping up
These three are my favorite test suites and also part of my favorite open source projects!
We’ve adopted Capybara as the official testing tool at PlataformaTec for some time already and I18n is one of the subjects of my upcoming book about Rails 3. In one specific chapter, we will build a tool that stores I18n translations into TokyoCabinet, which allows us to create and update translations through a web interface, similarly to ActiveRecord. The only difference is that TokyoCabinet is waaaay faster.
Finally, the fact you can mimic several of Rspec features using simple Ruby (like Capybara using shared example groups and I18n simply using modules) will be part of my talk in Euruko 2010 entitled DSL or NoDSL: The power is in the middle. The talk will show cases where DSLs mimics much of the behavior provided by Ruby and discuss what we are winning and/or losing in such cases.
Keep following us and, until the next blog post is out, we would love to hear in the comments which are your favorite test suites!
Tags: capybara, euruko, i18n, open source, rails3, railties, ruby, tests
Posted in English | View Comments

All
English only
Em português apenas


