Friday, December 7, 2007

Redirecting nginx to Tomcat.

I've got a JRuby on Rails application which is deployed on a Tomcat server. My Http server of choice is nginx. I wanted nginx to redirect one of my domains to the JRuby application. The default port that Tomcat listens on is 8080.

The following nginx configuration snippet solves this problem:

server {
server_name mydomain.com www.mydomain.com;
location / {
proxy_pass http://127.0.0.1:8080;
}
}

BTW, I never thought that my (basic) Russian language skills will be useful in my IT career. Most of the nginx related blog posts I found are in Russian language!

Thursday, October 18, 2007

Rails: How to unit test the formatting of model fields in the views.

In our Rails applications we often use Textile/RedCloth or some other libraries to have things like Post's content nicely formatted in the view. I use acts_as_textiled. This plugin simplifies the code a lot. All that is needed in order to make a certain field 'textilized' is this line:

class Post < ActiveRecord::Base
acts_as_textiled :content
end

The question that I want to focus on here is:

How to unit test that a certain field will be nicely formatted in the views?

lolcat - this not so hard
more funny pictures

As Jay Fields has already explained (he focused on testing validations in his article) there are two approaches:
The first one is based on listing all the cases that are important, like whether paragraph tags are correctly inserted or whether bold tags are correct. It may be sometimes useful but for me it's a code smell that for testing a single line in your codebase you need so many test lines of code. Additionally, the acts_as_textiled plugin is nicely tested/specified so why duplicate the code? I'd rather test some of those cases in my Selenium tests or functional tests and choose the second approach:

def test_content_is_nicely_formatted
Post.expects(:acts_as_textiled).with(:content)
load "#{RAILS_ROOT}/app/models/post.rb"
end

I'm using mocha here for setting an expectation that a class method called acts_as_textiled will be called (passing the field name argument) during the load time of the class file. This solution is not ideal. I don't like hardcoding the path here but I still find it simpler that the first approach.
This way testing of things like validations and acts_as_* calls becomes much simpler.

Tuesday, October 16, 2007

How to parse a chess PGN file using JRuby and Chesspresso

I have a lot of chess PGN files with many chess games inside. What I want, is to have some way of filtering this collection e.g. by player names or by opening. Ideally, I'd like to use either Ruby or Python to do that.

My first approach was to find a pure Ruby library and use it. Unfortunately, I couldn't find any good one. No luck with any Python module either. I didn't want to use .Net platform in this case, but I did a quick google search with no good results...

The last hope was the Java platform. This time I was lucky and found Chesspresso. A quick test proved that the library is mature, exposes a very nice API and is actually very fast.
Even though I have a lot of experience with Java language, I don't fancy implementing my projects in statically typed languages. I find Ruby or Python much more readable and testable.

My choice is now clear - JRuby.
include Java
require 'Chesspresso-lib'
include_class 'chesspresso.pgn.PGNReader'

pgn_reader = PGNReader.new 'twic675.pgn'

while game = pgn_reader.parse_game do
if game.white.include?('Shirov')
puts game
end
end
  • The output in my case is:
Shirov,A - Naiditsch,A 1/2-1/2 (2007.10.08)
Shirov,A - Almasi,Z 1-0 (2007.10.09)


deliverhappy.jpg
more funny pictures

Thursday, June 28, 2007

RSpec for acts_as_taggable

Requirements:

An empty post
- should be valid
- should have no tags
- should allow setting tags

A post with tags
- should show tags
- should be listed as tagged


Implementation:

class Post < ActiveRecord::Base
acts_as_taggable
end


RSpec:

describe "An empty post" do
before(:each) do
@post = Post.new
end

it "should be valid" do
@post.should be_valid
end

it "should have no tags" do
@post.tags.should be_empty
end

it "should allow setting tags" do
@post.tag_list = "tdd, rails"
@post.save
@post.tags.should_not be_empty
end
end

describe "A post with tags" do
before(:each) do
@post = Post.new(:tag_list=>"tdd, python, rails")
@post.save
end

it "should show tags" do
@post.tag_list.names.should == ["tdd", "python", "rails"]
end

it "should be listed as tagged" do
Post.find_tagged_with('tdd').should == [@post]
end
end


More details:
http://agilewebdevelopment.com/plugins/acts_as_taggable_on_steroids

Thursday, June 14, 2007

Testing Rails controllers with mock objects

One of the steps in my 15 TDD steps to create a Rails application article was using the mocking technique.
Mock objects were used in a test for a controller method. From the feedback I received I noticed that this technique is not very popular and requires a more detailed explanation. This article describes why you might want to use mock objects for testing your controllers and how it simplifies the testing setup and code. The mocking library I use in this article is Mocha.

Rails controllers

Before I go into details about testing Rails controllers, let's clarify what is the responsibility of a Rails controller.

Ideally, a controller method should only call one method on a model class. Sometimes the method on the model that we call is some kind of finder like Person.find(:all). The result of the method should then be passed to the view.
A Rails controller should be a thin layer between your model classes and the view part.

A sample implementation looks like that:

def learn
@word = Word.random
end

As we see, we call the 'random method on the Word class which represents some model part of our application. Then we pass the result to the view via @word.

Testing Rails controllers

Being aware of that, we know what we want to test:
A learn method should:

  1. call the Word.random method

  2. pass the result to the view


Problems with traditional approach to testing Rails controllers
Now, what if we simply construct the test like this:

def test_learn_passes_a_random_word
get 'learn'
assert_kind_of Word, assigns('word')
end

The first problem here is that it calls the Word.random method, thus accesses the (test) database. It means we need to have some fixtures loaded so that the random method can retrieve some word from it.
The second problem is that we only test the type (kind_of) of the object assigned to @word. It means we don't fully test the method. We could imagine an implementation of the learn method that passes the tests but does the wrong thing (by assigning any other object to @word). Not ideal.

Testing with mock objects

Fortunately, we can use mock objects here and solve both problems.
We will replace the Word.random method with a method that always returns an object that we prepared before. Another advantage is that:

We don't connect to the database from our unit tests which is A Good Thing.


The object that we prepare before can then be used to assert whether the one passed to the view was the same as the result of calling Word.random method.

random_word = Word.new
Word.expects(:random).returns(random_word)

As we see, we prepare a random_word object, which is simply a new instance of a Word class. The next line does two things:

  • expects(:random) - sets our expectation that the random method will be called

  • returns(random_word) - replaces the real implementation of the 'random' method with an implementation that always returns the random_word object.


Btw, we didn't have to create a new instance of Word here. We could have used Object() there and that would work fine. The crucial part is that we compare the objects we return from Word.random with the one assigned to @word.

What is an expectation?

An expectation is almost like an assertion. The difference is that we set it before the actual call to the method that we test. The test will fail if the expectation isn't met.

We now extend the test with a call to the 'learn' method that we test:

random_word = Word.new
Word.expects(:random).returns(random_word)
get 'learn'

This test should now pass with the following implementation:

def learn
@word = Word.random
end


We also want to test that the object was assigned to @word, so we need an additional line with assertion:

def test_learn_passes_a_random_word
random_word = Word.new
Word.expects(:random).returns(random_word)
get 'learn'
assert_equal random_word, assigns('word')
end


The assertion tests the equality of the random_word object and the @word (we access it using assigns('word')). It's possible only thanks to the fact that we replaced the real Word.random method and have a reference to the result of this method.
Benefits

  • not connecting to a database
  • no need for preparing fixtures
  • faster tests
  • controller tests are less fragile for model changes

Drawbacks

  • some developers may find the tests less readable
  • if you rename the model method, the controller tests will still pass
  • increased need for some integration tests - Selenium or standard Rails integration tests


Conclusion

I hope that this article sheds more light on what are mock objects. I strongly recommend trying to add some tests like that to your test suite. If you want to read more about mocking you should first go to the classic Martin Fowler's article on Mock Objects. After that go to the Jay Field's blog. You can also find some interesting examples on the Mocha website.

Thursday, May 31, 2007

How to programmatically submit a POST form using IronPython

Sometimes you may want to download a web page that is a result of submitting a form that uses a POST method. The code below shows how to do that using IronPython.

  1. prepare a request object, that is created for a given URI.
  2. write the PARAMETERS string to the request stream.
  3. retrieve the response and read from its stream.


URI = 'http://www.example.com'
PARAMETERS="lang=en&field1=1"

from System.Net import WebRequest
request = WebRequest.Create(URI)
request.ContentType = "application/x-www-form-urlencoded"
request.Method = "POST"

from System.Text import Encoding
bytes = Encoding.ASCII.GetBytes(PARAMETERS)
request.ContentLength = bytes.Length
reqStream = request.GetRequestStream()
reqStream.Write(bytes, 0, bytes.Length)
reqStream.Close()

response = request.GetResponse()
from System.IO import StreamReader
result = StreamReader(response.GetResponseStream()).ReadToEnd()
print result

Wednesday, May 30, 2007

How to download a web page using IronPython

Downloading a web page is a common programming task. Here are two snippets of code that show how to do it with IronPython. The code was tested on Windows and Mac (with Mono).

Using the WebClient class

from System.Net import WebClient
dataStream = WebClient().OpenRead('http://google.com')
from System.IO import StreamReader
reader = StreamReader(dataStream)
result = reader.ReadToEnd()
print result

Using the WebRequest and WebResponse classes

from System.Net import WebRequest
request = WebRequest.Create("http://google.com")
response = request.GetResponse()
responseStream = response.GetResponseStream()
from System.IO import StreamReader
result = StreamReader(responseStream).ReadToEnd()
print result

Monday, May 28, 2007

Selenium on Rails in 5 minutes

Introduction
Selenium is a very good framework for testing web applications. It's an ideal tool to use when you want to test your Rails apps from a user perspective. There is a Selenium on Rails plugin that simplifies creating and running Selenium tests.

Step 1. Create a Rails app and install the Selenium on Rails plugin

rails selenium_rocks
cd selenium_rocks
script/plugin install http://svn.openqa.org/svn/selenium-on-rails/selenium-on-rails

(it may take a while)

Step 2. Configuration
Create vendor/plugins/selenium-on-rails/config.yml file and paste the following:

environments:
- test
browsers:
safari: '/Applications/Safari.app/Contents/MacOS/Safari'
#firefox: 'c:\Program Files\Mozilla Firefox\firefox.exe'
#ie: 'c:\Program Files\Internet Explorer\iexplore.exe'

Change the browser path to point to your browser.

Step 3. Create a Selenium test

script/generate selenium welcome_page_test

Edit test/selenium/welcome_page_test.sel. Replace the existing content with the following:

setup
open '/'
assert_title 'Ruby on Rails: Welcome aboard'

Step 4. Start the server in a test environment
run in a new terminal:

script/server -e test

Step 5. Run the Selenium test

rake test:acceptance

You should see the following output:

1 tests passed, 0 tests failed

Congratulations!

More information:
Selenium On Rails website
Full-stack Web App Testing with Selenium and Rails (RailsConf 2007)

Friday, May 11, 2007

rcov for rails and how to analyze its reports

rcov is a code coverage tool for Ruby.

There are at least two reasons why it's worth using code coverage tools in your application.
1. It shows you which area of your code is untested.

This information can give you some clue, where it might be worth to cover the code with tests.

2. Gives you information about the code that is never executed.

You run your tests and the code coverage tool reveals a code that was not executed. It either means that this part was not tested or that the code is actually never used by your application. If it's the second case then you may simplify your codebase by removing the code. Don't worry, if you think you will later need it you can always find it your Subversion history. You use version control system, don't you?

rcov for Rails - installation

First, you install rcov gem.

gem install rcov

Then, you go to your application directory and install rcov for rails plugin:

./script/plugin install http://svn.codahale.com/rails_rcov

The plugin extends your Rake by adding new tasks like:

rake test:units:rcov
rake test:functionals:rcov

When you run 'rake test:units:rcov', it first runs all of your unit tests and then creates a coverage report. You can find it in the coverage/units directory.
It also outputs a simple text report:

5 tests, 13 assertions, 0 failures, 0 errors
+----------------------------------+-------+-------+--------+
| File | Lines | LOC | COV |
+----------------------------------+-------+-------+--------+
|app/controllers/application.rb | 7 | 3 | 100.0% |
|app/helpers/application_helper.rb | 3 | 2 | 100.0% |
|app/models/word.rb | 21 | 18 | 100.0% |
+----------------------------------+-------+-------+--------+
|Total | 31 | 23 | 100.0% |
+----------------------------------+-------+-------+--------+
100.0% 3 file(s) 31 Lines 23 LOC

How to analyze rcov results?

That's a result for the current code of our Words application. The only line that is interesting here is the one for word.rb. It says that the tests fully cover the Word class, which is a good thing.
When I run 'rake test:functionals:rcov' I get the following:

1 tests, 2 assertions, 0 failures, 0 errors
+-----------------------------------+-------+-------+--------+
| File | Lines | LOC | COV |
+-----------------------------------+-------+-------+--------+
|app/controllers/application.rb | 7 | 3 | 100.0% |
|app/controllers/words_controller.rb| 6 | 5 | 100.0% |
|app/helpers/application_helper.rb | 3 | 2 | 100.0% |
|app/helpers/words_helper.rb | 2 | 2 | 100.0% |
|app/models/word.rb | 21 | 18 | 22.2% |
+-----------------------------------+-------+-------+--------+
|Total | 39 | 30 | 53.3% |
+-----------------------------------+-------+-------+--------+

First, let's have a look at the second line, which says that we have 100% test coverage for the word_controller. That's nice. Have a look at the last line, related to the word.rb file. Why do we have only 22.2% here? Is that a problem? Does it mean that we have untested code? The answer is 'no'. We shouldn't be bothered about this line. Remember that test:functionals are tests for the controllers and this what we focus our tests on. We don't want to care about model classes here, they are already tested in test:units suite. In our case the reason for a low coverage is that we mock the Word.random method in our controller's tests. The real Word class is never called.

Summarizing, all we should care about in my opinion are results of coverage analysis for model classes when we run test:units and the results for controllers when we run test:functionals.
I suggest you give rcov a try. It should take you only 5 minutes to discover how your tests cover the code of the application. Who knows, maybe you can find some code that was never executed?

Thursday, May 10, 2007

... and some more TDD steps with Rails

We were working together with Marcin on the Words application. It was a lot of fun. We were pair programming for about two hours. First, we started by going through the last TDD steps again. That was good as an exercise and also it was a good introduction for Marcin (he was driving the keyboard during this part) to Mac, TextMate, Rake and Autotest. After we repeated all the steps, we decided that we want to have a way of feeding our database with more words. The idea was to have a form with a text box where a user could paste any kind of text. The text would then be parsed into words which are put into the database.
It was obvious that we need some kind of Word.add_content method. So we started with tests for that method. The final result of those tests is as following:

def assert_count_after_add(count, content)
Word.add_content(content)
assert_equal count, Word.count
end

def test_add_content
assert_count_after_add 2, ""
assert_count_after_add 3, "single"
assert_count_after_add 5, "two words"
assert_count_after_add 9, "four wordzz are funny"
assert_count_after_add 10, "duplicate duplicate"
end

As you can see there is a custom assertion. We also test that if there is already a word in the database we don't want it to be added again.
The implementation for the add_content:

def self.add_content content
if not content.empty?
content.split(" ").each {|eng_word|
if Word.find(:first,
:conditions => "eng = '#{eng_word}'") == nil
Word.new(:eng=>eng_word).save
end
}
end
end

It's not the prettiest piece of code but it should be fine for now.
Oops, it looks like we don't have a test for the fact that words are saved with their english translation only...
After implementing the add_content method we realized that now our database could be filled with words that are not translated (pl field is empty). It means we want to change the implementation of the 'Word.random' method so that it only displays words that are already translated. There is no point in displaying the english version only...
It sounds like we need word.translated? method. Let's write some tests:

def test_translated
assert !Word.new.translated?
assert (Word.new :pl=>"tak", :eng=>"yes").translated?
assert !(Word.new :pl=>"tak").translated?
assert !(Word.new :eng=>"yes").translated?
end

The implementation is simple:

def translated?
pl and eng
end

Now, it's time for adding a test for the fact that Word.random only returns translated words. Again, we call the test several times so that we can assume it works. We can probably refactor it later to some nicer way. We add a word to the database and then assert that it wasn't chosen even after 100 calls. Any ideas how to test it better?

def test_use_words_with_translation
Word.new( :pl=>'tak', :eng=>'yes').save
Word.add_content("hello")
randoms = []
100.times {randoms << Word.random.eng}
assert (randoms.include? "hello") == false
end

The new implementation looks like that:

def self.random
(Word.find(:all).select {|word|word.translated?}.sort_by {rand})[0]
end

Unfortunately, there was no time to create a user interface for adding a content. Sounds, like a nice topic for the next session.

Wednesday, May 9, 2007

15 TDD steps to create a Rails application




Hi,

Testing Rails applications is my passion for over 7 years now. If you sign up to this newsletter you will receive exclusive information about everything related to Ruby unit testing, Rails acceptance testing, JavaScript testing, testable architectures, TDD, BDD and good OO design. 


  Subscribe to the Testing Rails mailing list


Introduction

Several times recently, I have been asked how to develop a Rails application using the Test Driven Development approach. I'm not an expert here, but I've put together some notes on how to start working on a Rails application whilst being test-driven all the time.
As an example I will use a word-learning web application. The simplest use case is to display a random word object (with its Polish translation) from the database.
Every time we refresh we want to see a different word.

1. Create a new Rails application
rails my_app
cd my_app

Run tests with 'rake test'. It fails due to missing database configuration.

2. Set up the databases - config/database.yml
The code below assumes sqlite3 databases.
development:
adapter: sqlite3
database: db/my_app_development.sqlite

test:
adapter: sqlite3
database: db/my_app_test.sqlite

'rake test' now runs fine.

3. Create a Word class with a corresponding unit test
script/generate model Word

4. Write a unit test for the Word class. Edit the test/unit/word_test.rb.
def test_word_is_english_and_polish
word = Word.new :eng=>'never', :pl=>'nigdy'
assert_equal 'never', word.eng
assert_equal 'nigdy', word.pl
end

'rake test' now fails due to missing words table.

5. Edit db/migrate/001_create_words.rb
We are using a migration here in order to create a table. It's a recommended way of dealing with database changes.

def self.up
create_table :words do |t|
t.column :eng, :string
t.column :pl, :string
end

Word.new(:eng=>'yes', :pl=>'tak').save
Word.new(:eng=>'no', :pl=>'nie').save
Word.new(:eng=>'everything', :pl=>'wszystko').save
end

def self.down
drop_table :words
end

The sample words that we are adding use Word.new .. lines, will be added to the development database. It's important to distinguish the 'test' and 'development' database. The first one is only used during tests. The latter is used by default when you start the application.

Apply the migration with 'rake db:migrate'.

'rake test' now succeeds with the following:
'1 tests, 2 assertions, 0 failures, 0 errors'


6. Fixtures and test for word.random. Edit word_test again.
It's not easy to test a method which behaves randomly. Let's assume that it's enough to test that if we have only two words in our database then one of them should be called at least once per 10 calls.
fixtures :words
def test_random
results = []
10.times {results << Word.random.eng}
assert results.include?("yes")
end
Note the 'fixtures :words' line. Edit the 'words.yml' file.
yes:
id: 1
pl: 'tak'
eng: 'yes'
no:
id: 2
pl: 'nie'
eng: 'no'
This will be loaded to the test database before every run of tests. 7. Implement the Word.random method
def self.random
all = Word.find :all
all[rand(all.size)]
end
Warning: The code above could be slow for many words in a database (we retrieve all words only for selecting a random element). It's good enough for our needs. 8. Generate the Words controller with a 'learn' action
script/generate controller Words learn
9. Write a test for the learn method Just as there is a one-to-one ratio between unit tests and models, so there is between functional tests and controllers. The Controller's responsibility is to retrieve objects from the Model layer and pass them to the View. Let's test the View part first. We use the 'assigns' collection which contains all the objects passed to the View.
def test_learn_passes_a_random_word
get 'learn'
assert_kind_of Word, assigns('word')
end
10. Make the Test Pass
def learn
@word = Word.new
end
11. Write more tests in the words_controller_test How can we test that controller uses the Word.random method? We don't want to duplicate the tests for the Word.random method. Mocks to the rescue! We will only test that the controller calls the Word.random method. The returned value will be faked with a prepared word. Let's install the mocha framework:
gem install mocha
Now we can use 'expects' and 'returns' methods. 'expects' is used for setting an expectation on an object or a class. In this case we expect that the 'random' method will be called. We also set a return value by using 'returns' method. Setting a return value means faking (stubbing) the real method. The real Word.random won't be called. If an expectation isn't met the test fails.
require 'mocha'

def test_learn_passes_a_random_word
random_word = Word.new
Word.expects(:random).returns(random_word)
get 'learn'
assert_equal random_word, assigns('word')
end
'rake test' now fails. The Word.method wasn't called. 12. Rewrite the implementation
def learn
@word = Word.random
end
'rake test' now passes. 13. Test that a word is displayed: Extend the existing test with assert_tag calls.
def test_learn_passes_a_random_word
random_word = Word.new(:pl=>'czesc', :eng=>'hello')
Word.expects(:random).returns(random_word)
get 'learn'
assert_equal random_word, assigns('word')
assert_tag :tag=>'div', :child => /czesc/
assert_tag :tag=>'div', :child => /hello/
end
14. Implement the view - learn.rhtml
<div>
<%= @word.eng %>
<%= @word.pl %>
</div>
15. Manual testing
script/server
Go to 'http://localhost:3000/words/learn'. Refresh several times. Related articles ... and some more TDD steps with Rails Testing Rails controllers with mock objects If you want to read more about testing in Rails go to the Guide To Testing The Rails.

If you read this far you should Follow andrzejkrzywda on Twitter and subscribe to my RSS

Thursday, April 19, 2007

Test your web application with Selenium

Thanks to Skills Matter I attended a Selenium session tonight. Selenium is a tool for automated web testing. The session was led by Erik Doernenburg. He's a ThoughtWorker, so I knew the presentation was going to be good quality. Not only did he present what Selenium is, but he also presented many good techniques for using it with your project.
Some of my notes below:
  • All important browsers are supported
  • All important languages are supported
    • He used Java at the talk
  • SeleniumIDE is the tool for recording tests
    • It's not necessary to use it, you can write tests in your favourite language
    • SeleniumIDE is only for Firefox
  • You need to start the Selenium server in order to run tests
  • Many methods available:
    • createCookie
    • dragdrop
    • fireEvent
    • selenium.click("btnG")
    • assertTitle
  • There is a concept of locators
    • It means the way to locate your DOM elements
    • name, id, identifier
    • document.forms['myForm'].myDropDown
    • XPath
    • link text
    • css selectors, css = a[href="#id3"]
  • Selenium server sets the proxy in your browser, so your browser asks the server for everything
  • It's bad for performance testing
  • Good for testing multiple browsers
  • There was some great advice, which I think applies for all kinds of functional tests
    • "Test what you want to test"
    • Which means, if you have already tested creating users in a functional way, you don't need to test it functionally again
    • So, you can actually use your production code to change the state and then functionally test what you're actually testing
    • Saves time
    • Makes your tests less fragile
    • Cool!
  • It's a good pattern to have classes for all of your pages
    • WelcomePage
    • DailyViewPage
  • And one class for your test
    • AddItemToBasket
    • CreateUser
  • Your test classes will then use your page classes
    • welcomePage = WelcomePage()
    • welcomePage.setDailyView()
  • There are other ways of functionally testing your web application
    • HttpUnit which asks your web server, not the browser
    • Use a Presentation pattern
      • have only thin presentation layer
      • test only your "event handlers" methods
  • It's common practice for QA people to create Selenium scenarios
  • You can start your TDD session with a failing Selenium test

Monday, April 16, 2007

RuPy 2007

  • A well-organized conference
  • It was interesting to meet so many "dynamic" people
  • Many Ruby people attended Python talks and vice-versa
  • Maybe instead of having two separate sessions it would be better to have one with shorter talks?
  • Poznan is a very nice city, almost as nice as Wroclaw :)
  • The quote of the conference:
    • "Look, all of the Ruby people have Macs!"
    • "Yeah, they are better paid..."


Christopher Arndt talk on TurboGears
  • TG is very similar to Rails
  • TG has support for testing, unfortunately it wasn't shown during the talk
  • as with all (?) Python web frameworks, you can't use Python for implementing views
  • it's better with Rails, that you don't have to change the language for your views
  • It uses SqlObject which is ok, but doesn't give you the same level of abstraction as SQLAlchemy (ActiverRecord-like ORM library) does
  • They want to switch to SQLAlchemy soon, cool!
Ruby/Rails tools that help
  • by Cloves Carneiro Jr.
  • a very good presentation
  • the speaker is a good example of a happy Rails programmer ;-)
  • I was already familiar with all of the tools, but I learnt some details
  • Capistrano
  • Rake
  • Subversion
  • autotest (zentest)
    • perfect integration with growl notifier, very cool!
  • rcov
  • TextMate

Grono.net talk
  • as far as I know Grono.net is the biggest Django application in the world
  • many performance challenges
  • "Share nothing" architecture
  • Lots of caching
  • The first version was in Java - "unmantainable", switched to Python
  • not many tests
  • experiments with WSGI tests
  • they use an old Django version, can't take advantage of new features (testing support)
RadiantCMS
  • a nice introduction to RadiantCMS
  • it has a good system of extensions
  • it's a Rails application, so you can easily extend it with your models/controllers
  • there is a wysiwyg plugin

Domain specific languages with Ruby by Jan Szumiec
  • Jan presented a very agile way of creating a valid DSL
  • The pair programming part (with Olle Jonsson) was very funny :)

Developing with IronPython and Windows Forms by us (Michael Foord and Andrzej Krzywda)
  • The talk was a little bit too long (90 minutes)
  • I enjoy coding live :)

Thursday, March 8, 2007

Andrzej on Test Driven Development

Test Driven Development helps me creating better software. TDD is not only about testing. It's more about designing and managing scope. I'll try to show what I mean by using a simple example based on tabbedimages application.

Tabbedimages is a simple image viewer. We are going to add drag and drop feature to it.
I start with a requirement, which I analyze and spike for a working solution. Based on the analysis I create a user story and an automated acceptance test. The acceptance test "drives" me when I'll add unit tests and the production code.
  • Requirement
    • tabbedimages is a simple image viewer.
    • We want to add a new feature to tabbedimages.
  • Title.
    • 'Drag&drop support'.
    • TIP: It's good to have a short title for a user story.
  • Analysis: let's list things to worry about:
    • Single image files
    • Multiple image files
    • Directories
    • Non-image files
    • Already open image.
  • Spiking
    • TIP: This part should give us better understanding of the problem
      • If we're sure how to implement the new feature we can skip this step.
    • tabbedimages is implemented using IronPython and Windows Forms.
      • The code is availalable here.
    • Google for 'drag and drop windowsforms' and see some code examples.
    • Check out the fresh version of tabbedimages.
    • Try to add the required feature to our code base (without tests).
    • Discover that Windows Forms has support for DragDropEffect.
      • Which displays a 'plus' sign if the thing that we're trying to drag is acceptable.
    • Add the DragDropEffect to the list of things to worry about.
  • User story:
    • Marten wants to drag and drop his images from his desktop to tabbedimages.
    • He starts tabbedimages.
    • He then drags the 'Faye001.jpg' file over the application.
    • The plus sign appears.
    • He drops it.
    • A new tab is created with a label saying 'Faye001.jpg'
    • 'She's cute' he thinks
    • Marten realizes that there are more Faye's pictures.
    • He drags 'Faye001.jpg' (again) and 'Faye002.jpg'.
    • He drops them.
    • Two new tabs are created.
    • He drags and drops readme.txt file.
    • The message box appears saying 'readme.txt doesn't appear to be a valid image file'
    • He quits tabbedimages.
  • Functional test:
    • Write the ideal code (DSL-like) that follows the user story steps:
    • marten.starts()
      he.asserts_number_of_tabs(0)
      he.drags('Faye001.jpg')
      assert shows_plus()
      he.drops('Faye001.jpg')
      he.asserts_number_of_tabs(1)
      he.asserts_tab_labels(['Faye001.jpg'])
      fayes_pictures = ['Faye001.jpg', 'Faye002.jpg']
      marten.drags_and_drops(fayes_pictures)
      marten.drops(fayes_pictures)
      he.asserts_number_of_tabs(3)
      he.asserts_tab_labels(['Faye001.jpg', Faye001.jpg', Faye002.jpg'])
      he.drags_and_drops('readme.txt')
      he.sees_message_box("readme.txt doesn't appear to be a valid image file")
      he.quits()
  • Implementation
    • Run the Functional Test (FT).
    • Whenever FT fails or you can think of any edge cases not covered by FT:
      • Write appropriate Unit Test that reflects the problem.
      • Add the implementation to fix the problem.
After the last phase the drag & drop feature should be ready to use. Of course, this example presented a very simple problem. Hopefully, the Test Driven Development as shown here should be easy to understand. In the near future, I'll try to write more about the implementation phase, including some refactoring techniques, so stay tuned!

Sunday, March 4, 2007

Doctests for showing snippets of code

From Public

  • djangosnippets.org shows many snippets of Python/Django code along with doctests.
  • Have a look at partitioning lists example.
  • This is what I think a good usage of doctests.
  • I'm not sure about using them *instead* of unittests.
  • Michael is also not sure
  • BTW, I find many of those Python code samples really pretty.
    • I couldn't find any with the string 'self' inside.
    • Maybe that's why I like them ;-)

def partition(thelist, n):
"""
Break a list into ``n`` pieces. The last list may be larger than the rest if
the list doesn't break cleanly. That is::

>>> l = range(10)

>>> partition(l, 2)
[[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]

>>> partition(l, 3)
[[0, 1, 2], [3, 4, 5], [6, 7, 8, 9]]

>>> partition(l, 4)
[[0, 1], [2, 3], [4, 5], [6, 7, 8, 9]]

>>> partition(l, 5)
[[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]]

"""
try:
n = int(n)
thelist = list(thelist)
except (ValueError, TypeError):
return [thelist]
p = len(thelist) / n
return [thelist[p*i:p*(i+1)] for i in range(n - 1)] + [thelist[p*(i+1):]]

Sunday, February 25, 2007

Writing Domain Specific Languages with Python

  • The process of writing DSL is the same in all languages.
    • You write an ideal spec.
    • Then, you try to fit the spec so that it's kind of valid syntax for the language you use.
  • An example of ideal domain language (well, maybe not so ideal, but quite good):

test '/Login':
GetReq(uname = 'a_user',
password = '')
expect_tmpl: 'login.tmpl'
expect_tmpl_has: 'errorMsg'
expect_tmpl_data: { 'uname':'a_user',
'missing_fields' : ['Password']}
expect_session_lacks: 'uid'
  • Now the same but as a valid Python syntax:

test_serv('/Login',
GetReq(uname = 'a_user',
password = ''),
expect_tmpl = 'login.tmpl',
expect_tmpl_has = 'errorMsg',
expect_tmpl_data = { 'uname':'a_user',
'missing_fields' : ['Password']},
expect_session_lacks = 'uid')

  • Useful tricks inclue:
    • keyword args (expect_tmpl_data=...)
    • unbounded arg lists (*args)
    • operator overloading __add__(self, other_form)
    • reflection via hasattr, getattr, vars and friends
  • Thanks to Dan Milstein for his talk at PyCon about Little Languages in Python.

IronPython and Windows Forms talk

The presentation in html version

TabbedImages application

  • We got some good feedback after the presentation.
  • To my surprise there were many people already playing with IronPython.
  • What's even more surprising, there were some people that have already used Windows Forms.
  • Watch the tabbedimages application, I'm going to extend it with some interesting features.
  • If you browse the sources for it, you'll find some simple tests in the MainTest.py.
  • They exist because I'm afraid I'm not able to not do Test Driven Development.

Saturday, February 24, 2007

PyCon 2007

The PyCon conference is really great.
So far I was at the following talks:
  • Internationalization
  • Parsing revisited
  • Developing desktop applications with Dabo
    • I didn't know that creating applications with wxPython is such a pain.
    • The Dabo core is simply a layer above the wxPython.
    • The application they used on the presentation was an image viewer.
    • The API was really nice and in many cases reminded me a simplified version of WinForms API (where you uses strings instead of overused in .Net enumerations).
    • wxPython (so Dabo as well) works on all important platforms (Windows, Linux, MacOS)
  • WSGI - an introduction
    • Because there are many different Python web frameworks, WSGI goal is to be able to integrate different layers of different applications.
    • I'm still not sure how it could be used in the Ruby world and if there is any need for that.
  • Writing parsers and compilers with PLY
    • Recently, I'm working quite a lot with parsers and PLY is the main tool we use.
    • It's very good, very efficient, very elegant
    • You declare your grammar rules using docstrings
  • Creating the WhatWhat project with TurboGears
    • WhatWhat is a very simple project tracking tool that was implemented in 4 days using TurboGears.
    • It seems that frameworks like TurboGears are becoming quite easy to learn and use.
    • Not sure if as easy as Ruby on Rails, though.
    • I'm still not convinced what could be the reason for me not to use Rails in web applications :)
  • pyweek: making games in 7 days
  • Creating games with Pygame on the GP2X
  • SQLAlchemy
    • Looks like a very powerful ORM tool for Python.
    • Reminds me of Hibernate.
    • There is a tool called Elixir which is a layer on top of SQLAlchemy and is more similar to the Ruby ActiveRecord.
    • Migrations are only available as extensions.
    • However, they are different than Ruby migrations. I didn't quite understand what is really the difference. I was told that Ruby migrations are dangerous because they do too many things for you (!?).
    • It seems to me the above is one of the fundamental differences between Python and Ruby programmers.
  • IronPython: Present and future
    • That was definitely the best talk so far, IMO.
    • Jim is doing a great work in Microsoft.
    • My name even appeared at his slide :-)
    • He wasn't able to read my name, though ;-)
    • What's so difficult with my name? - Andrzej Krzywda
    • There were screenshots of Michael's tutorial on IronPython and Windows Forms.
    • Seo was mentioned, of course. He is the author of FePY - the Comunnity Edition of IronPython.
    • The reason of this special edition is that MS can't easily apply patches that are sent by non-MS people.
    • IronPython is supported by the MS Robotics project.
    • Jim presented a small IP script where he used some MS Gaming API to create a simple 3d game.
    • There is a growing support for IronPython in ASP.NET
  • Embedding Little Languages in Python
    • Little Languages is what the rest of the world calls Domain Specific Languages.
    • The talk was about how to use Python to create such a DSL.
    • The idea is the same as always with DSL:
      • Firstly, you think of an ideal specification.
      • You try to use the language features to fit to the spec.
    • Python features that are helpful:
      • Keyword args
      • * args
      • getattr, hasattr
    • The speaker used _ convention in his code examples.
    • I also prefer it, but it doesn't seem to be so popular in the Python community (it is in Ruby community).

Friday, January 19, 2007

Groovy and SwingBuilder

In my comparison of IronPython, Groovy and JRuby I used a "naked" Swing API to create a GUI application using Groovy. Groovy comes with a nice library called SwingBuilder that simplifies a lot of work with Swing. I couldn't find any documentation for SwingBuilder but the source code is pretty straight-forward. In my opinion, the best thing in the SwingBuilder version is the way you declare that a form has a button:

def frame = swing.frame(defaultCloseOperation: WC.EXIT_ON_CLOSE) {
button('OK', actionPerformed: click)
}

You can pass a closure to the frame method.
I really like this way. It allows you to create your GUI in a more declarative way.

As was suggested by Martin C. Martin and Dierk Koenig (many thanks!) on the Groovy-user list, the code might look something like the following:

import javax.swing.WindowConstants as WC

click = { event ->
event.source.text = "Pressed!"
}

def swing = new groovy.swing.SwingBuilder()
def frame = swing.frame(
size: [200, 200],
defaultCloseOperation: WC.EXIT_ON_CLOSE,
title: 'Hello' ) {
button('OK', actionPerformed: click)
}

frame.show()

Thursday, January 11, 2007

Iron Python on Balloons

Have you ever wondered how to create a simple application that uses balloons to notify about some events? Below is the result of my spike on how I could use IronPython and Windows Forms to create such thing.



Basically, it should:

  1. show an icon in the system tray

  2. NotifyIcon is a class that allows you to display an icon in the system tray.

  3. display a tooltip every n milliseconds

  4. It was easily achieved by using the notifyIcon.ShowBalloonTip method and the Timer (and its Tick event) class.

  5. have a context menu that allows you to exit the application

  6. The ContextMenu class.


Just paste the code to the Main.py file, prepare a test.ico file and run it with:
ipy Main.py

import clr

clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Drawing import Icon
from System.Windows.Forms import (Application, ContextMenu,
MenuItem, NotifyIcon, Timer)


class Main(object):

def __init__(self):
self.initNotifyIcon()
timer = Timer()
timer.Interval = 60000
timer.Tick += self.onTick
timer.Start()


def initNotifyIcon(self):
self.notifyIcon = NotifyIcon()
self.notifyIcon.Icon = Icon("test.ico")
self.notifyIcon.Visible = True
self.notifyIcon.ContextMenu = self.initContextMenu()


def onTick(self, sender, event):
self.notifyIcon.BalloonTipTitle = "Hello, I'm IronPython"
self.notifyIcon.BalloonTipText = "Who are you?"
self.notifyIcon.ShowBalloonTip(1000)


def initContextMenu(self):
contextMenu = ContextMenu()
exitMenuItem = MenuItem("Exit")
exitMenuItem.Click += self.onExit
contextMenu.MenuItems.Add(exitMenuItem)
return contextMenu


def onExit(self, sender, event):
self.notifyIcon.Visible = False
Application.Exit()


if __name__ == "__main__":
main = Main()
Application.Run()

Update:Marcin shows how to achieve the same with Groovy.