Robert Basic's blog

Introducing pugdebug

by Robert Basic on April 1st, 2015

In my spare time in the past few months I was working on a tool that would help me in my every day job as a PHP programmer. As you may, or may not, know, I'm using vim as my editor/almost IDE, but one thing that is missing from it is the ability to debug PHP files remotely. Yes, there are a bunch of plugins out there that add debugging to vim, but none of them felt usable for me.

And based on my google searches, there are no standalone remote debuggers for PHP, that work on Linux.

In February this year I started to work on a desktop application that would help me address this issue.

pugdebug

pugdebug  is a PyQt desktop application meant to be used as a remote debugger for PHP, that communicates with xdebug.

It is meant to be a debugger and only a debugger. There are a plenty of (good) IDEs that include remote debugging and I'm not going to start writing another one (although I did start one, once).

The application is still pretty simple, ugly as hell, but it works. Sort of. There are still a few kinks left to sort out and I'm doing my best to write them all down.

It's dependencies are Python 3.4, Qt 5.4, SIP 4.6 and PyQt 5.4. The read me file should have a bit more details on how to start using it. I know it's a bit messy to set everything up, but I am working on building executables for different Linux distros. That stuff is hard!

It is lincesed under the GNU GPL v3 license, because PyQt.

Using pugdebug

Start the application, click the start button and then it waits for incomming connections. Load a PHP page to start a HTTP debug session, and pugdebug should break on the first line of your code.

Stepping around the code is possible with the step into, step out and step over commands. At each step, pugdebug will get the current variables state from xdebug and display them.

Double clicking on lines in the code viewer will set breakpoints on those lines. Do note though that there needs to be a debugging session active to be able to set a breakpoint. This will change in the (near) future.

And that's about it. While I know it doesn't look like much, this was, and still is, a nice learning experience for me and the best part of it is that I was using pugdebug earlier this week to debug a PHP application I'm working on.

Tags: pugdebug, python, pyqt, qt, debugging, xdebug.
Categories: Software, Programming, Development.

Vimprovements

by Robert Basic on March 26th, 2015

One part where I always felt (and still feel) that I could improve on my Vim usage, is when moving around. I don't use the mouse, I don't use the arrow keys, but for a while now I felt that spamming hjkl to get from one place to another is not really efficient. To be honest, it is kind of easy to just press down jjjjjjj ... to move down lines. Went a bit too far? Just kk back. But there has to be a better way.

First off, I installed vim-hardtime to break the habit of spamming hjkl. That plugin limits the number of times one can press hjkl in a set time frame. I have it set up to block me from moving for a second after pressing the same motion twice in a row. jj is OK, but if I want to do jjj, well...

And this gets annoying. I want to edit code, not sit around and wait!

Faster moving around

I know about relative line numbers. I tried using them. And I guess my brain is just not wired in such a way that I could find relative numbers easy to use.

The second plugin I installed is vim-easymotion. It is supposed to make moving around in Vim much easier. There's a lot of stuff going on in there, it has a lot of features. For now, I use 2 bits from it - jump to anywhere with <leader>s and <leader>k and <leader>j to move in lines up and down. I especially like these in visual mode, makes selecting text real nice.

I started using : <cr> more to jump to specific lines, f and F to search on the current line, { and } to move in paragraphs. I'm also more comfortable with general search with / and ?.

One other pair of commands that help me reduce spamming jk is <ctrl>u and <ctrl>d to scroll the window up and down.

Faster working with files

The third plugin I installed is CtrlP. I stumbled upon it in a /r/vim thread and decided to try it out. Up until then I was using NERDTree exclusively to navigate around files and I can't really remember when was the last time I hit <ctrl>n to open it since I have CtrlP. It's just amazing to work with files now.

NERDTree still has it's use cases, for example in a project where I don't really know what files are in there, but for projects on which I work frequently... Oh boy! Wonderful stuff.

I left the default <ctrl>p for searching for files, but remapped <tab> to open CtrlP's find buffer mode.

Misc

Other stuff I did to make working in Vim more comfortable...

I set leader to be <space>. I had it as , for a long while, but realised that it's to close on the keyboard to the . and I kept repeating the last command when I actually wanted to do some magical incantation.

I learned about :m[ove] to move and :co[py] to copy lines. Good stuff.

dap and cap pairs are also useful from time to time. They just simply delete, or change, around the current paragraph.

Finally, I added the following bit to my .vimrc to highlight the current line:

augroup CursorLine
    au!
    au VimEnter,WinEnter,BufWinEnter * setlocal cursorline
    au WinLeave * setlocal nocursorline
augroup End

So, that's pretty much it. I don't think that I'm super fast navigating around in vim, but it's definitely better than some 3 months ago.

Next thing I want to tackle is using registers, and especially macros.

And read more of that nice :help holy-grail.

Tags: vim, plugins, ctrlp, movements.
Categories: Blablabla, Software, Development.

Install PyQt5 in Python 3 virtual environment

by Robert Basic on February 4th, 2015

It's been a while since I last made something with PyQt, so I decided to check out what's it like nowadays. I'm curious to see what's new in Qt5 and how does it differ from Qt4. Qt5 also can run under python 3 so I figured to give it a try.

Fedora 21 comes with both python 2.7 and python 3.4, but the default version is 2.7, which means if PyQt5 is installed through the package manager, it will be installed against 2.7. As I'm not currently in the mood of bricking my laptop by changing the default python version, I decided to install PyQt5 in a python virtual environment. Btw, Fedora 22 should have python 3 by default.

In the code samples below, assume the working directory is always ~/pyqt.

Create a virtual environment

First off, let's create a virtualenv with python 3.4:

virtualenv --python=python3.4 env

Activate the virtualenv and check the python version to verify:

source env/bin/activate
python --version

And that should print something like Python 3.4.1. Leave the virtualenv active, as that's where PyQt5 is going to be installed.

PyQt5 dependencies

Cool, now with that set up, let's get PyQt5 dependencies sorted out:

sudo yum install gcc gcc-c++ python3-devel qt5-base qt5-base-devel

As the documentation says, SIP must be installed before PyQt5. Lets grab the sources, configure and make and install them.

wget http://sourceforge.net/projects/pyqt/files/sip/sip-4.16.5/sip-4.16.5.tar.gz
tar xzf sip-4.16.5.tar.gz
cd sip-4.16.5
python configure.py
make
sudo make install
cd ..
rm -r sip-4.16.5*

Not sure why I had to do sudo make install. Verify sip is installed correctly by starting a python shell and typing in the following:

import sip
sip.SIP_VERSION_STR

That should show the sip version 4.16.5.

Installing PyQt5

All the dependencies should be met now, so let's install PyQt5.

wget http://sourceforge.net/projects/pyqt/files/PyQt5/PyQt-5.4/PyQt-gpl-5.4.tar.gz
tar xzf PyQt-gpl-5.4.tar.gz
cd PyQt-gpl-5.4
python configure.py --qmake /usr/bin/qmake-qt5
make
make install
cd ..
rm -r PyQt-gpl-54*

This will install PyQt5 with the basic modules such as QtCore, QtWidgets and QtSql. Check the output of the python configure.py step to see what modules will be installed. If you need additional modules in your PyQt5 setup, you'll have to install additional Qt packages on your system. For example, to get the QtWebKit module, install the qt5-qtwebkit package through your package manager first.

Writing a basic PyQt5 app we can verify that it all works. Save the following as pyqt.py:

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = QMainWindow()
    window.show()
    sys.exit(app.exec_())

Running it with python pyqt.py should start the application that's just one small window.

Happy hacking!

Tags: python, pyqt5, virtualenv.
Categories: Software, Development, Programming.

Configuring read-only and read-write access to bitbucket repos

by Robert Basic on January 30th, 2015

Trying to automate things on my server, I ended up needing read-only for one group of my bitbucket repos and read-write access to another group.

On bitbucket, if read-only access is required for a repository, a deployment key can be added for that repository.

Create one ssh key that will be a deployment, read-only key:

user@server$ ssh-keygen -f ~/.ssh/id_rsa_ro -t rsa -C "email@domain.com"

and add it to repositories needing read-only access.

Create a second ssh key that will be used for repositories needing read and write access:

user@server$ ssh-keygen -f ~/.ssh/id_rsa_rw -t rsa -C "email@domain.com"

and add it as an ssh key under your bitbucket account.

Next, configure ssh a bit, telling it what identity to use for what host by adding something like this to the ~/.ssh/config file:

Host bitbucket.org-ro
    HostName bitbucket.org
    IdentityFile ~/.ssh/id_rsa_ro

Host bitbucket.org-rw
    HostName bitbucket.org
    IdentityFile ~/.ssh/id_rsa_rw
Host 

With all that in place, for repositories where read-only access is needed, set the remote url for the origin like:

git remote set-url origin git@bitbucket.org-ro:user/repo_with_ro_access.git

and where read-write access is needed:

git remote set-url origin git@bitbucket.org-rw:user/repo_with_rw_access.git

Now for repositories with the bitbucket.org-ro hostname I have read-only access and for repositories with the bitbucket.org-rw hostname read and write access. Neat.

Tags: bitbucket, ssh, keys, setup.
Categories: Development.

Mocking hard dependencies with Mockery

by Robert Basic on December 23rd, 2014

One problem with unit testing legacy applications is that the code has new statements all over the place, instantiating new objects in a way that doesn't really makes it easier to test the code.

Of course, the easy answer to this is "Just refactor your application!", but that's almost always easier said than done.

If refactoring is an option, do it. If not, one option is to use Mockery to mock the hard dependencies.

One prerequisite to make this work is that the code we are trying to test uses autoloading.

Let's take the following code for an example:

<?php
namespace App;
class Service
{
    function callExternalService($param)
    {
        $externalService = new Service\External();
        $externalService->sendSomething($param);
        return $externalService->getSomething();
    }
}

The way we can test this without doing any changes to the code itself is by creating instance mocks by using the overload prefix.

<?php
namespace AppTest;
use Mockery as m;
class ServiceTest extends \PHPUnit_Framework_TestCase {
    public function testCallingExternalService()
    {
        $param = 'Testing';

        $externalMock = m::mock('overload:App\Service\External');
        $externalMock->shouldReceive('sendSomething')
            ->once()
            ->with($param);
        $externalMock->shouldReceive('getSomething')
            ->once()
            ->andReturn('Tested!');

        $service = new \App\Service();

        $result = $service->callExternalService($param);

        $this->assertSame('Tested!', $result);
    }
}

If we run this test now, it should pass. Mockery does it's job and our App\Service will use the mocked external service instead of the real one.

The problem whit this is when we want to, for example, test the App\Service\External itself, or if we use that class somewhere else in our tests.

When Mockery overloads a class, because of how PHP works with files, that overloaded class file must not be included otherwise Mockery will throw a "class already exists" exception. This is where autoloading kicks in and makes our job a lot easier.

To make this possible, we'll tell PHPUnit to run the tests that have overloaded classes in separate processes and to not preserve global state. That way we'll avoid having the overloaded class included more than once. Of course this has it's downsides as these tests will run slower.

Our test example from above now becomes:

<?php
namespace AppTest;
use Mockery as m;
/**
 * @runTestsInSeparateProcesses
 * @preserveGlobalState disabled
 */
class ServiceTest extends \PHPUnit_Framework_TestCase {
    public function testCallingExternalService()
    {
        $param = 'Testing';

        $externalMock = m::mock('overload:App\Service\External');
        $externalMock->shouldReceive('sendSomething')
            ->once()
            ->with($param);
        $externalMock->shouldReceive('getSomething')
            ->once()
            ->andReturn('Tested!');

        $service = new \App\Service();

        $result = $service->callExternalService($param);

        $this->assertSame('Tested!', $result);
    }
}

And that should be pretty much it. If nothing else, it should make parts of old code easier to test.

For anyone interested, I put the example code up on Github.

Tags: mockery, php, testing, unit tests.
Categories: Development, Programming.