Lexer Tumblog

Lexer is a startup building a suite of innovative apps designed to measure marketing efficiency and effectiveness.

March 11, 2010 at 10:39am

Expectations on method calls within a block in RSpec

At Lexer, we are using Solr (via SunSpot) to perform full text searches on our data. We have a Rails app that has a feature where a user can dynamically build a query and submit it via an AJAX call with the query itself represented as a JSON array of conditions.

The server side receives this JSON array, and passes it through module called JsonToSunspot which executes the equivalent Sunspot search.

So I’d receive a set of conditions as JSON that look something like this:

[
    {'with' => {'some_attribute' => 'foo'}},
    {'with_not' => {'some_other_attribute' => 'bar'}}
]

And translate them into a Sunspot invokation like this:

Sunspot.search(MyModel) do
  with(:some_attribute, 'foo')
  with_not(:some_other_attribute, 'bar')
end

Ideally, I’d like to unit test all of this without actually bringing up Sunspot. The problem was, I couldn’t see how to mock ‘self’ within the context of the block in order to set expectations that ‘with’ and ‘with_not’ were called with appropriate arguments.

Turns our you can do this with RSpec’s ‘and_yield’ method like so:

Sunspot.should_receive(:search).with(MyModel).and_yield do |context|
  context.should_receive(:with).with(:some_attribute, 'foo')
  context.should_receive(:with_not).with(:some_other_attribute, 'bar')
end

The key here is the ‘context’ variable. It is a mockable version of self. Pretty awesome huh?! Now when my JSON-to-Sunspot code executes, I can actually test that it invokes Sunspot correctly without having it running.

Special thanks to @robertpostill for the tip.

February 18, 2010 at 9:31am

Ignite Sydney

Lexer’s very own Andrew Harvey will be presenting at Ignite Sydney 4 on Tuesday 2nd March. While he won’t be talking about anything to do with the products we’re building, we’re looking forward to seeing what comes out of his talk titled “Your best camera is the one you have with you”.

Apparently there are only limited tickets left (they’re free!), so register now!

February 13, 2010 at 12:35pm

Installing pyactivemq on OS X

Some of our apps need to have high performance messaging queues to hand data between various processes in an efficient manner. We decided after a lot of research to go with ActiveMQ, which has been an excellent choice, but installing the python bindings on OS X proved a challenge. Here is how we got it done.

Note that pyactivemq is only compatible with ActiveMQ-CPP 2.x.x. Don’t bother installing 3.x.x. It will only make you cry.

Download the latest ActiveMQ-CPP 2.x.x build. Apache has annoying redirects from http downloads so it is generally just easier to get the subversion checkout. For me that meant doing this:

svn co https://svn.apache.org/repos/asf/activemq/activemq-cpp/tags/activemq-cpp-2.2.6   

Having done that, it’s just a matter of a straight build, except for a mysteriously missing config directory. We’ll create that first.

cd activemq-cpp-2.2.6
mkdir config
./autogen.sh
./configure
make
sudo make install

Now, that was easy enough. Here comes the fun. I’m assuming you’re using python installed with MacPorts into the default location. Something funky happens with the symlinks there, so we’re going to hack around with the Apple install of Python so it points back at the MacPorts one.

cd /System/Library/Frameworks/Python.framework/Versions
sudo mv 2.6 2.6.apple
sudo ln -s /opt/local/Library/Frameworks/Python.framework/Versions/2.6 2.6

Sweet. I’m not sure of the full implications of doing this, but it doesn’t appear to have broken anything for me yet, but I only use my MacPorts build of Python, not the Apple one.

Now let’s install boost. It takes forever, so go do something with yourself while waiting.

sudo port install boost +python26

Now for pyactivemq.

svn co http://pyactivemq.googlecode.com/svn/tags/pyactivemq-0.1.0
cd pyactivemq-0.1.0

Now you would think it’s just a matter of running setup.py and installing? No. We have to mess around with it because it has some nasty hard-coded paths. I’m assuming you let ActiveMQ-CPP install into the standard path (/usr/local/lib), so those are the paths I used, but you will have to work out your own paths if you did it differently.

This is the patch from the changes I made to setup.py, replacing nasty hard-coded paths with more nasty hard-coded paths.

Index: setup.py
===================================================================
--- setup.py    (revision 208)
+++ setup.py    (working copy)
@@ -67,19 +67,21 @@
         ]
 else:
     include_dirs = [
-        '/opt/activemq-cpp-2.2.1/include/activemq-cpp-2.2.1'
+        '/usr/local/include/activemq-cpp-2.2.6',
+        '/opt/local/include'
         ]
     libraries = [
         'activemq-cpp',
         'uuid',
-        'boost_python'
+        'boost_python-mt'
         ]
     library_dirs = [
-        '/opt/activemq-cpp-2.2.1/lib'
+        '/usr/local/lib',
+        '/opt/local/lib'
         ]
     extra_compile_args = []
     extra_link_args = [
-        '-Wl,-rpath,/opt/activemq-cpp-2.2.1/lib'
+        '-Wl,-rpath,/usr/local/lib'
         ]
     define_macros = [
         ('BOOST_PYTHON_NO_PY_SIGNATURES', 1),

There you have it. Assuming all your paths match up, you can go ahead and build and install.

sudo python setup.py install

Then load up your python interpreter and make sure the module imports happily.

~/Projects/pyactivemq-0.1.0 $ python
Python 2.6.4 (r264:75706, Jan  5 2010, 09:51:33) 
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyactivemq
>>> pyactivemq
<module 'pyactivemq' from '/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/pyactivemq.so'>

February 12, 2010 at 11:38am

First post.

As the description says, Lexer is a startup building a suite of apps to measure and increase marketing efficiency. This is where we’re going to share some thoughts, tricks we’ve discovered along the way and things we think are cool.

Stay tuned for more!