-
Navigate files easily with ctrlp.vim
Few years ago I used a lightweight IDE called “Sublime Text 2”. And one of the most compelling features of it was an ability to switch between files by hitting
Ctrl
+P
and typing in a part of the file name.This is exactly what
ctrlp.vim
implements in vim. Usage is extremely simple: install the plugin, hitCtrl
+P
, type a part of the file name, select a desired file and hitEnter
. You can use arrow keys orCtrl
+J
andCtrl
K
to navigate between suggested file list.
Source: https://github.com/kien/ctrlp.vim.
-
Making Django and Lettuce play nice together
Lettuce is a great BDD tool which allows you to parse expressions written via Gherkin syntax in python. However the documentation is not very comprehensive, and at the moment current version (0.2.19) has some issues integrating with the latest Django (1.6.1 at the moment of writing). Without further ado, I’ll get to a comprehensive tutorial.
Let’s assume you are using
pip
andvirtualenv
for the dependency control, and you already have a working project configured. Your project is called “myproject”, and the only app you have within your project is called “polls”.Setup
First, you have to install
lettuce
library. At the moment of writing, current released version (0.2.19) has an error integrating with Django, so we’ll install current development version. Releases 0.2.20 and up should include the fix, sopip install lettuce
would be better if the version is out.pip install -e \ git://github.com/gabrielfalcao/lettuce@cccc397#egg=lettuce-master pip install django-nose splinter pip freeze > requirements.txt
First line downloads lettuce package from the github repository and installs missing dependencies. You can replace
cccc397
with the current commit. Technically commit can be omitted, but we don’t want to have an unstable ever-changing branch in ourrequirements.txt
. I also addeddjango-nose
since nose assertions come in handy while writing Lettuce steps, as well assplinter
, which is a great tool for testing web application.Add Lettuce to the
INSTALLED_APPS
in yourmyproject/settings.py
:INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.admin', 'django.contrib.admindocs', # ... third party apps ... 'lettuce.django', 'myproject', 'polls', )
You also have to explicitly specify the apps you want to use with lettuce:
LETTUCE_APPS = ( 'polls', )
By default, lettuce will run its’ tests against your default database. But we want to use test database for that, so we have to add few more settings:
LETTUCE_TEST_SERVER = 'lettuce.django.server.DjangoServer' LETTUCE_SERVER_PORT = 9000
Where
LETTUCE_TEST_SERVER
is a subclass of Django’sLiveTestServerCase
- a class which runs a test server for you andLETTUCE_SERVER_PORT
is different from port 8000 so you won’t have issues running the development server viapython manage.py runserver
at the same time as running Lettuce tests.You also have to create a
features
directories inside the apps you want to test with Lettuce:/myproject /myproject __init__.py settings.py urls.py wsgi.py /polls /features /steps __init__.py polls_list.py polls_list.feature __init__.py models.py tests.py views.py manage.py requirements.txt terrain.py
Lettuce has its’ own settings file called
terrain.py
. It has to be in the same directory as amanage.py
:from django.core.management import call_command from django.conf import settings from lettuce import before, after, world from splinter.browser import Browser @before.each_scenario def flush_database(scenario): call_command('flush', interactive=False, verbosity=0) @before.each_scenario def prepare_browser(scenario): world.browser = Browser() @after.each_scenario def destroy_browser(scenario): world.browser.quit()
This code flushes the database before each scenario, as well as prepares and destroys the
splinter
browser.Writing the features
Feature files support standard Gherkin syntax, let’s write one right now in
polls/features/polls_list.feature
:Feature: Polls list Scenario: Polls list without any polls When I access the "polls list" url Then I see a text "We didn't find any polls!" Scenario: Polls list with one poll Given a poll with the title "Hello world" When I access the "polls list" url Then I see a text "Hello world" And I do not see a text "We didn't find any polls!"
Now describe the steps in
polls/features/steps/polls_list.py
:from django.core.urlresolvers import reverse from lettuce import step, world from lettuce.django import django_url from nose.tools import assert_in, assert_not_in from polls.models import Poll PAGES = { "polls list": "polls:list" } @step(r'access the "(.*)" url') def access_the_url(step, name): world.browser.visit(django_url(reverse(PAGES[name]))) @step(r'see a text "(.*)"') def see_a_text(step, text): assert_in(text, world.browser.html) @step(r'a poll with the title "(.*)"') def create_a_poll_with_the_title(step, title): poll = Poll.objects.create(title=title) polls.save() @step(r'do not see a text "(.*)"') def do_not_see_a_text(step, text): assert_not_in(text, world.browser.html)
Running the tests
Now, you can run
python manage.py harvest --test-server
to run the tests you just wrote:$ python manage.py harvest --test-server Creating test database for alias 'default'... Django's builtin server is running at 0.0.0.0:9000 Feature: Polls list Scenario: Polls list without any polls When I access the "polls list" url Then I see a text "We didn't find any polls!" Scenario: Polls list with one poll Given a poll with the title "Hello world" When I access the "polls list" url Then I see a text "Hello world" And I do not see a text "We didn't find any polls!" 1 feature (1 passed) 2 scenarios (2 passed) 6 steps (6 passed) Destroying test database for alias 'default'...
Don’t forget the
--test-server
switch - otherwise Lettuce will run tests against your default database.Sources
You can find some more details on Lettuce and Django integration here: Web development fun with Lettuce and Django.
Update
Rather than using
--test-server
switch, it’s easier and safer to set a flag in yoursettings.py
(suggested by Michel Sabchuk):LETTUCE_USE_TEST_DATABASE = True
This way you won’t end up accidentally erasing your production database after forgetting to add
--test-server
flag. -
Open previously edited file in vim
You can open previous location by hitting
Ctrl-O
. You can prefix the command with a number to go multiple files back. You can also travel forward in “file history” by usingCtrl-I
.There’s a nice article on Vim Wikia with more details on a subject.
-
Old file version in split with vim
If you use git VCS, you can view previous version of the file you are currently editing in a split window by executing following command:
:vsp tmp | read !git show HEAD^:path/from/working/directory
For the instant syntax highlighting, you can specify temporary file’s extension, like following:
:vsp tmp.py | read !git show HEAD^:lib/module.py
You can also cycle back by few versions by replacing
HEAD^
(which points to the previous commit) withHEAD~N
, whereN
is the number of commits you would like to go back in history by. For example, if you would like to get a version of the file 4 commits ago - you can do so by executing following command::vsp tmp | read !git show HEAD~4:path/from/working/directory
It’s a pretty nice hack when you need to quickly view previous version of the file you are working on.
-
Pasting with vim and indentation issues
If you use vim often - you probably had to paste something into vim from the outside source. And, if you have corresponding indentation rules, they will get applied, turning your nice block of code into something that looks more like a case of stairs:
def foo(a, b): a, b = b, a print "I am doing something important." return a - b
Quite nasty, isn’t it? But that’s where vim’s
paste
option comes in. Before pasting, execute:set paste
. If you go into insert mode, you’ll see the usual mode indicator switch to-- INSERT (paste) --
. Try pasting the same block of code now:def foo(a, b): a, b = b, a print "I am doing something important." return a - b
Beautiful. Don’t forget to switch back to a regular mode by executing
:set nopaste
.