Ben Murden

Tag: Development

Django Test Mock Factory

by on Sep.29, 2015, under Development

There are many ways to use Python Mock’s patcher, but sometimes there’s something in your test module you’ll always need to mock. It’s possible to apply the patcher to the entire class, but it still means you have to litter your tests with parameters. To make things a bit tidier and DRYer, I started using the following mock factory in my tests to create instance-level mocks.

def add_mock(self, target, *args, **kwargs):
    target_obj = target.split('.')[-1]
    patcher = patch(target, *args, **kwargs)
    if target_obj[0].isupper():
        # Assume class mock
        attr = 'Mock'
    else:
        attr = 'mock_'

    attr += target_obj

    setattr(self, attr, patcher.start())
    self.addCleanup(patcher.stop)

The above attribute factory will add new instance level attributes, named either mock_some_variable for imports that look like variables or functions, or MockSomeObject for imports that look like objects. It will use the patcher’s patch function directly, and assign the new attribute to the current test object instance, then stop the patch at clean up.

This can be used in your tests’ setup method like so.

def setUp(self):
    module = 'my_app.management.commands.send_monthly_report'
    self.add_mock('%s.settings' % module)

    # Pass patch arguments through the setup method
    self.add_mock('%s.send_mail' % module, autospec=True)

    # We now have an instance-level mock to work with
    self.mock_settings.REPORT_TEMPLATE = 'test-template'

Now all your test methods have instance attributes to work with instead of a long line of parameters. Much better!

def test_the_first(self):
    function_to_test()
    self.assertTrue(self.mock_send_mail.called)
3 Comments :, , more...

Regex to find CSS Class Uses

by on Oct.01, 2014, under Development, Web

I needed this to find uses of a specific CSS class in order to replace them all with a namespaced version.

class=(["a-z\-]*[" ]+)(nav)([ "])

Optionally use the replacement text below, complete with variable markers. In this case, “nav” will be prefixed with “forum-“.

class=$1forum-$2$3
1 Comment :, , , more...

Giant Bomb Video Commander

by on Mar.08, 2013, under Development

I recently wrote a Chrome Extension, partially because I wanted to learn how one can be used to inject functionality into an existing website, and partially because I really wanted some functionality that wasn’t already on Giant Bomb. From this was born the Giant Bomb Video Commander! It’s a simple extension that injects some Javascript when there’s a video present, and adds keyboard controls for HTML5 video objects.

I’ve posted a GitHub repository of the source as well.

Leave a Comment :, , , more...

CakePHP ACL and Groups

by on Sep.23, 2011, under Development

There are many great tutorials out there to help you get started with the arcane ACL features of CakePHP, but none of them seemed to cover a specific problem that occurs with reassignment of a parent_id on a save operation.

The parent_id would revert to being null on some save operations on the User model, so if, for example, the user changed their password, they’d end up with no group and an infinite redirect; the redirect loop occurring because everyone should be in a group. Upon investigation with a debugger on task, I discovered the problem resides with the parentNode function, and that all other examples out there are written with the expectation that a group_id will be present in every save operation.

Since parentNode is always called on a save operation performed on a model acting as an ARO, some modification of the typical example is necessary to ensure that you’ll always have the group_id available for finding the parent node ID.

function parentNode() {
  if (!$this->id && empty($this->data)) {
    return null;
  }
  $data = $this->data;
  if (empty($this->data)) {
    $data = $this->read();
  }
  if (empty($data['User']['group_id'])) {
    $groupId = $this->field('group_id', array(
      'id' => $this->id,
    ));
    if (empty($groupId)) {
      return null;
    } else {
      $node = array('model' => 'Group', 'foreign_key' => $groupId);
    }
  } else {
    $node = array('model' => 'Group', 'foreign_key' => $data['User']['group_id']);
  }
  return $node;
}

Like in the Change Password example, on pages where user data is changed, but the group_id is not needed, the parent_id can now be found for the User ARO. On the other hand, you won’t overwrite the group_id if the data indicated it should be changed.

Hope people find this useful, and if you have any suggestions for improvements, let me know!

Leave a Comment :, , more...

Support IE6 With the Back of Your Hand

by on Apr.13, 2011, under Development, Web

I’ve been doing a bunch of development work with CakePHP lately and have gotten to the point where I’m in a position to submit a few bug fixes and improvements back to the project. One small improvement has already been accepted, so that’s a great start! Working with Oracle, PHP, CentOS, and CakePHP has been an… interesting experience, so I’m hoping to get some information together about that process and make it a little easier for others in the future.

In other areas, I’ve been trying out Sass, Compass, and blueprint, with exciting results. If you wish for CSS to be more pragmatic and structured, these tools are for you. I’m particularly interested in the potential of very experimental CSS3 implementations, like transform and transition. Makes the web feel modern all over again.

When asked to support IE6, I would do it, but I’d do it like this. I actually rather like the idea behind the Universal IE6 CSS project, in that it still aims to provide a usable and clearly presented version of your site, substituting only CSS, but lets those users know they’re getting a much reduced experience. If they’re on the Internet, why haven’t they hopped on the future train yet? Come on people, get with the program. The fact that projects like this have to exist is pretty crazy, but now that we’re in this mess, I’m glad it does.

Leave a Comment :, , , , , , more...

CakePHP, Rolling Session IDs, and Concurrent Ajax Calls

by on May.04, 2010, under Development

Picture a set of buttons in a list that allow users to quickly cancel listed bookings. These buttons make an Ajax call to a controller action with the relevant ID passed as a parameter. I already have a jQuery method turning these buttons – generated with Cake’s HtmlHelper->Link() – into Ajax calls using the anchor’s href as the URL.

By itself, each button works perfectly. The problem shows itself when a user tries to cancel multiple bookings concurrently. The controller action will process the first request, but choke on all the rest and return a blank response.

This is a very specific problem I had encountered with CakePHP, but I think with Ajax becoming the norm for web application interaction, it’s worth mentioning this fix. The problem lies in the way session IDs are regenerated between requests in Cake’s highest security setting. An easy fix would be to set Cake’s core security level to medium, but we care too much about security, right? So we’ll make a reusable fix that allows us to overcome this issue without affecting security across the board.

In the controller:

function beforeFilter() {
    // Fix for concurrent Ajax requests
    // If it's an Ajax request and the session id has been passed, retain it
    if ($this->RequestHandler->isAjax() && !empty($this->params['url']['s'])) {
      $this->Session->id($this->params['url']['s']);
      $this->Session->start();
    }
    parent::beforeFilter();
  }

This is a modification of a solution given by Adam Royle, changing the process to be more reusable with an optional querystring variable that indicates we want to keep the session ID during an Ajax call. This means if you don’t expect concurrent requests, you can forego the querystring and your action will perform as normal without it, regenerated session IDs included.

The link can then be generated like this:

echo $html->link(__('Cancel', true), array('controller' => 'bookings', 'action' => 'cancelBooking', '?' => array('s' => $session->id()), $booking['id']), array('id' => 'cancelLink'.$booking['id']));

Thanks be to Adam Royle for discovering the root of the issue.

Improvements

This is still not a perfect solution.

  • It effectively exposes the temporary session ID to the user.
  • It will also still fall over if the session ID is regenerated by another Ajax call on the same page, meaning all Ajax buttons in a single view must have this logic.
  • If the user navigates away and performs other actions, then returns to this page using the browser’s back button – and therefore, the local cache – the session ID will be wrong again and cause a logout.

Other possible solutions:

  • Queueing Ajax requests in jQuery so the session ID can regenerate as needed.
  • Session handler in Cake is modified to not regenerate session IDs when request is from an XML/HTTP Request Object.

If there are any other ways of preventing newly generated session IDs from messing with concurrent Ajax requests, I’d love to hear them!

5 Comments :, , , , more...

I was going to use some Wave pun, but…

by on Dec.02, 2009, under Web

I’m sure all the Wave puns have already been made, and I’m not about to further muddy that particular pool. Google Wave is at least deserving of some attention, having just played around with the preview for a couple days now. It at least meets the original idea of being a good evolution of e-mail, with everything being conversation based as opposed to discrete individual messages that steadily grow in size as reply text is copied each time. Like some kind of out-of-control text-sponge being passed around, e-mails often reached the point where they rarely, if ever, related to the original message and became a form of delayed IM system. That’s an oxymoron, I know, but the advantage e-mails always had over IM was that they were not tied to one particular system or application.

Google Wave addresses the e-mail issue while introducing the very same IM issues. It is currently a closed system, and while the help topics do say the development team hope to implement traditional e-mail support in the future, I wonder how well that can work with the dynamic functions of Google Wave. Either Wave sends out periodic updates to “e-mail subscribers” of a particular Wave, or the Wave itself is limited due to the presence of e-mail participants. I’m sure the developers have thought about all this, though I’d still like to see what they come up with.

Besides the lack of backwards-compatibility with any existing system, the features of Google Wave do seem pretty exciting, as long as all your friends also have an account.

Leave a Comment :, , more...

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!