Future relevancy protection: As Tim Garnett correctly points out, at lot of discussion of Refinements suffers from not being clear about which version of Ruby is current at the time of writing. When I gave this talk, the latest version of Ruby was 2.2.3, but I believe the content is still relevant for 2.3.
Chances are, you’ve heard of refinements, but never used them.
The Refinements feature has existed as a patch and subsequently an official part of Ruby for around five years, and yet to most of us, it only exists in the background, surrounded by a haze of opinions about how they work, how to use them and, indeed, whether or not using them is a good idea at all.
I’d like to spend a little time taking a look at what Refinements are, how to use them and what they can do.
But don’t get me wrong - this is not a sales pitch for refinements! I am not going to try to convince you that they will solve all your problems and that you should all be using them!
The title of this presentation is “Why is nobody using refinements?” and that’s a genuine question. I don’t have all the answers!
My only goal is that, by the end of this, both you and I will have a better understanding of what they actually are, what they can actually do, when they might be useful and why it might be that they’ve lingered in the background for so long.
What are refinements?
Simply put, refinements are a mechanism to change the behaviour of an object in a limited and controlled way.
By change, I mean add new methods, or redefine existing methods on an object.
By limited and controlled, I mean that adding or changing those methods does not have an impact on other parts of our software which might interact with that object.
Refinements are defined inside a module, using the refine method.
This method accepts a class – String, in this case – and a block, which contains all the methods to add to that class when the refinement is used. You can refine as many classes as you want within the module, and define as many methods are you’d like within each block.
To use a refinement, we call the using method with the name of the enclosing module.
And that’s really all there is to refinements – two new methods, refine, and using.
However, there are some quirks, and if we really want to properly understand refinements, we need to explore them. And the best way of approaching this, is by considering a few more simple examples.
Now we know that we can call the refine method within a module to create refinements, and that’s all relatively straightforward, but it turns out that where and when you call the using method has a profound effect on how the refinement behaves with our code.
We’ve seen that invoking using inside a class definition works. We activate the refinement, and we can call refined methods on a String instance:
Our class uses the refinement, but when we pass a block to a method in that class, suddenly it breaks.
So what’s going on here? For many of us this is quite counter-intuitive; after all, we’re used to being able to re-open classes, or share behaviour between super- and sub-classes, but it seems like that only works intermittently with refinements?
It turns out that the key to understanding how and when refinements are available relies on another aspect of how Ruby works that you may have already heard of, or even encountered directly.
The key to understanding refinements is understanding about lexical scope.
To understand about lexical scope, we need to learn about some of the things that happen when Ruby parses our program.
Let’s look at the first example again:
As Ruby parses this program, it is constantly tracking a handful of things to understand the meaning of the program. Exploring these in detail would take a whole presentation in itself, but for the moment, the one we are interested in is called the “current lexical scope”.
Let’s “play computer” and follow Ruby as it processes our simple program here.
The top-level scope
When Ruby starts parsing the file, it creates a new structure in memory – a new “lexical scope” – which holds various bits of information that Ruby uses to track what’s happening at that point. We call this the “top-level” lexical scope.
When we encounter a class (or module) definition, as well as creating the class and everything that involves, Ruby also creates a new lexical scope, nested “inside” the current one.
We can call this lexical scope “A”, just to give it an easy label. Visually it makes sense to show these as nested, but behind the scenes this relationship is modelled by each scope linking to its parent. “A”’s parent is the top level scope, and the top level scope has no parent.
As Ruby processes all the code within this class definition, the “current” lexical scope is now A.
When we call using, Ruby stores a reference to the refinement within the current lexical scope. We can also say that within lexical scope “A”, the refinement has been activated.
We can see now that there are no activated refinements in the top-level scope, but our Shouting refinement is activated for lexical scope A.
Next, we can see a call to the method shout on a String instance. The details of method dispatch are complex and interesting in their own right, but one of the things that happens at this point is that Ruby checks to see if there are any activated refinements in the current lexical scope that might affect this method.
In this case, we can see that for current lexical scope “A”, there is an activated refinement for the shout method on Strings, which is exactly what we’re calling.
Ruby then looks up the correct method body within the refinement module, and invokes that instead of any existing method.
And there, we can see that our refinement is working as we hope.
So what about when we try and call the method later? Well, once we leave the class definition, the current lexical scope returns to being the parent, which is the top-level one.
Then we find our second String instance and a method being called on it.
Once again, when ruby dispatches for the shout method, it checks the current lexical scope – the top-level one – for the presence of any refinements, but in this case, there are none. Ruby behaves as normal, which is to call method_missing and this will raise an exception by default.
Calling using at the top-level
If we had called using Shouting outside of the class, at the top level, our use of the refined method both inside and outside the class works perfectly.
This is because once a refinement is activated, it is activated for all nested scopes, so calling using at the top level activated the refinement in the top level scope, which means it will be activated in any nested scopes, including “A”. And so, our call to the refined method within the class works too.
So this is our first principle of how refinements work:
When we activate a refinement with the using method, that refinement is active in the current and any nested lexical scopes.
However, once we leave that scope, the refinement is no longer activated, and Ruby behaves as it did before.
Lexical scope and re-opening classes
Let’s look at another example from earlier. Here we define a class, and activate the refinement, and later re-open that class and try to use it. We’ve already seen that this doesn’t work; the question is why.
Watching Ruby build its lexical scopes reveals why this is the case. Once again, the first class definition gives us a new, nested lexical scope A. It’s within this scope, that we activate the refinements. Once we reach the end of that class definition, we return to the top level lexical scope.
When we re-open the class, Ruby creates a nested lexical scope as before, but it is distinct from the previous one. Let’s call it B to make that clear.
While the refinement is activated in the first lexical scope, when we re-open the class, we are in a new lexical scope, and one where the refinements are no longer active.
So our second principle is this:
Just because the class is the same, doesn’t mean you’re back in the same lexical scope.
This is also the reason why our example with subclasses didn’t behave as we might’ve expected:
It should be clear now, that the fact that we are within a subclass actually has no bearing on whether or not the refinement is activated; it’s entirely determined by lexical scope. Any time Ruby encounters a class or module definition via the class (or module) keywords, it creates a new, fresh, lexical scope, even if that class (or module) has already been defined somewhere else.
This is also the reason why, even when activated at the top-level of a file, refinements only stay activated until the end of that file – because each file is processed using a new top-level lexical scope.
So now we have another two principles about how lexical scope and refinements work.
Just as re-opened classes have a different scope, so do subclasses. In fact:
The class hierarchy has nothing to do with the lexical scope hierarchy.
We also now know that every file is processed with a new top-level scope, and so refinements activated in one file are not activated in any other files – unless those other files also explicitly activate the refinement.
Lexical scope and methods
Let’s look at one more of our examples from earlier:
Here we are activating a refinement within a class, and defining a method in that class which uses the refinement. Later, we create an instance of the class and call our method.
We can see that even though the method gets invoked from the top level lexical scope – where our refinement is not activated – our refinement still somehow works. So what’s going on here?
When Ruby processes a method definition, it stores with that method a reference to the current lexical scope at the point where the method was defined. So when Ruby processes the greet method definition, it stores a reference to lexical scope A with that:
When we call the greet method – from anywhere, even a different file – Ruby evaluates it using the lexical scope associated with its definition. So when Ruby evaluates ”hello".shout inside our greet method, and tries to dispatch to the shout method, it checks for activated refinements in lexical scope “A”, even if the method was called from an entirely different lexical scope.
We already know that our refinement is active in that scope, and so Ruby can use the method body for “shout” from the refinement.
This gives us our fourth principle:
Methods are evaluated using the lexical scope at their definition, no matter where those methods are actually called from.
Lexical scope and blocks
A very similar process explains why our block example didn’t work. Here’s that example again – a method defined in a class where the refinement is activated yields to a block, but when we call that method with a block that uses the refinement, we get an error.
We can quickly see which lexical scopes Ruby has created as it processed this code. As before, we have a nested lexical scope “A”, and the method defined in our class is associated with it:
However, just as methods are associated with the current lexical scope, so are blocks (and procs, lambdas and so on). When we define that block, the current lexical scope is the top level one.
When the run method yields to the block, Ruby evaluates that block using the top-level lexical scope, and so Ruby’s method dispatch algorithm finds no active refinements, and therefore no shout method.
Our final principle
Blocks – and procs, lambdas and so on – are also evaluated using the lexical scope at their definition.
With a bit of experimentation, we can also demonstrate to ourselves that even blocks evaluated using tricks like instance_eval or class_eval retain this link to their original lexical scope, even though the value of self might change.
This link from methods and blocks to a specific lexical scope might seem strange or even confusing right now, but we’ll see soon that it’s precisely because of this that refinements are so safe to use.
But I’ll get to that in a minute. For now, let’s recap what we know:
Lexical scope & principles recap
Refinements are controlled using the lexical scope structures already present in Ruby.
You get a new lexical scope any time you do any of the following:
entering a different file
opening a class or module definition
running code from a string using eval
As I said earlier: you might find the idea of lexical scope surprising, but it’s actually a very useful property for a language; without it, many aspects of Ruby we take for granted would be much harder, if not impossible to produce. Lexical scope is used as part of how Ruby understands references to constants, for example, and also what makes it possible to pass blocks and procs around as “closures”.
We also now have the five basic principles that will enable us to explain how and why refinements behave the way they do:
Once you call using, refinements are activated within the current, and any nested, lexical scopes
The nested scope hierarchy is entirely distinct from any class hierarchy in your code; subclasses and superclasses have no effect on refinements; only nested lexical scopes do.
Different files get different top-level scopes, so even if we call using at the very top of a file, and activate it for all code in that file, the meaning of code in all other files is unchanged.
Methods are evaluated using the current lexical scope at their point of definition, so we can call methods that make use of
refinements internally from anywhere in the rest of our codebase.
Blocks are also evaluated using the lexical scope, and so it’s impossible for refinements activated elsewhere in our code to change the behaviour of blocks — or indeed, any other methods or code — written where that refinement wasn’t present.
Right! So now we know. But why should we even care? What are refinements actually good for? Anything? Nothing?
Let’s try to find out.
Let’s use refinements
Now, another disclaimer: these are just some ideas – some less controversial than others – but hopefully they will help frame what refinements might make easier or more elegant or robust.
The first will not be a surprise, but I think it’s worth discussing anyway.
Monkey-patching is the act of modifying a class or object that we don’t own – that we didn’t write. Because Ruby has open classes, it’s trivial to redefine any method on any object with new or different behaviour.
The danger that monkey-patching brings is that those changes are global – they affect every part of the system as it runs. As a result, it can be very hard to tell which parts of our software will be affected.
If we change the behaviour of an existing method to suit one use, there’s a good chance that some distant part of the codebase – perhaps hidden within Rails or another gem – is going to call that method expecting the original behaviour (or its own monkey-patched behaviour!), and things are going to get messy.
Say I’m writing some code in a gem, and as part of that I want to be able to turn an underscored String into a camelized version. I might re-open the String class and add this simple, innocent-looking method to make it easy to do this transformation.
Unfortunately, as soon as anyone tries to use my gem in a Rails application, their test suite is going to go from passing, not to failing but to ENTIRELY CRASHING with a very obscure error:
/app/.bundle/gems/ruby/2.1.0/gems/activesupport-4.2.1/lib/active_support/inflector/methods.rb:261:in `const_get': wrong constant name Admin/adminHelper (NameError)
from /app/.bundle/gems/ruby/2.1.0/gems/activesupport-4.2.1/lib/active_support/inflector/methods.rb:261:in `block in constantize'
from /app/.bundle/gems/ruby/2.1.0/gems/activesupport-4.2.1/lib/active_support/inflector/methods.rb:259:in `each'
from /app/.bundle/gems/ruby/2.1.0/gems/activesupport-4.2.1/lib/active_support/inflector/methods.rb:259:in `inject'
from /app/.bundle/gems/ruby/2.1.0/gems/activesupport-4.2.1/lib/active_support/inflector/methods.rb:259:in `constantize'
from /app/.bundle/gems/ruby/2.1.0/gems/activesupport-4.2.1/lib/active_support/core_ext/string/inflections.rb:66:in `constantize'
from /app/.bundle/gems/ruby/2.1.0/gems/actionpack-4.2.1/lib/abstract_controller/helpers.rb:156:in `block in modules_for_helpers'
from /app/.bundle/gems/ruby/2.1.0/gems/actionpack-4.2.1/lib/abstract_controller/helpers.rb:144:in `map!'
from /app/.bundle/gems/ruby/2.1.0/gems/actionpack-4.2.1/lib/abstract_controller/helpers.rb:144:in `modules_for_helpers'
from /app/.bundle/gems/ruby/2.1.0/gems/actionpack-4.2.1/lib/action_controller/metal/helpers.rb:93:in `modules_for_helpers'
You can see the error at the top there - something to do with constant names or something? Looking at the backtrace I don’t see anything about a camelize method anywhere?
The first is breaking API expectations. We can see that Rails has some expectation about the behaviour of the camelize method on String, which we obviously broke when we added our own monkey-patch elsewhere.
The second is that monkey patching can make it far harder to understand what might be causing unexpected or strange behaviour in our software.
Refinements in Ruby address both of these issues.
If we change the behaviour of a class using a refinement, we know that it cannot affect parts of the software that we don’t control, because refinements are restricted by lexical scope.
We’ve seen already that refinements activated in one file are not activated in any other file, even when re-opening the same classes. If I wanted to use a version of camelize in my gem, I could define and use it via a refinement, but anywhere that refinement wasn’t specifically activated – which it won’t be anywhere inside of Rails, for example – the original behaviour remains.
It’s actually impossible to break existing software like Rails using refinements. There’s no way to influence the lexical scope associated with code without editing that code itself, and so the only way we can “poke” some refinement behaviour into a gem is by finding the source code for that gem and literally inserting text into it.
This is exactly what I meant by limited and controlled at the start.
Refinements also make it easier to understand where unexpected behaviour may be coming from, because they require an explicit call to using somewhere in the same file as the code that uses that behaviour. If there are no using statements in a file, we can be confident – assuming nothing else is doing any monkey-patching – that Ruby will behave as we would normally expect.
This is not to say that it’s impossible to produce convoluted code which is tricky to trace or debug – that will always be possible – but if we use refinements, there will always be a visual clue that a refinement is activated.
Onto my second example.
Managing API changes
Sometimes software we depend on changes its behaviour. APIs change in newer versions of libraries, and in some cases even the language can change.
For example, in Ruby 2, the behaviour of the chars method on Strings changed from returning an enumerator to returning an Array of single-character strings.
Imagine we’re migrating an application from Ruby 1.9 to Ruby 2 (or later), and we discover that some part of our application which depends on calling chars on a String and expecting an enumerator to be returned.
If some parts of our software rely on the old behaviour, we can use refinements to preserve the original API, without impacting any other code that might have already been adapted to the new API.
Here’s a simple refinement which we could activate for only the code which depends on the Ruby 1.9 behaviour:
The rest of the system remains unaffected, and any dependencies that expect the Ruby 2 behaviour will continue to work into the future.
My third example is probably familiar to most people.
One of the major strengths of Ruby is that its flexibility can be used to help us write very expressive code, and in particular supporting the creation of DSLs, or “domain specific languages”. These are collections of objects and methods which have been designed to express concepts as closely as possible to the terminology used by non-programmers, and often designed to read more like human language than code.
Adding methods to core classes can often help make DSLs more readable and expressive, and so refinements are a natural candidate for doing this in a way that doesn’t leak those methods into other parts of an application.
RSpec as a DSL
RSpec is a great example of a DSL for testing. Until recently, this would’ve been a typical example of RSpec usage:
One hallmark is the emphasis on writing code that reads fluidly, and we can see that demonstrated in the line developer.should be_happy, which while valid Ruby, reads more like English than code. To enable this, RSpec used monkey-patching to add a should method to all objects.
Recently, RSpec moved away from this DSL, and while I cannot speak for the developers who maintain RSpec, I think it’s fair to say that part of the reason was to avoid the monkey-patching of the Object class.
However, refinements offer a compromise that balances the readability of the original API with the integrity of our objects.
It’s easy to add a should method to all objects in your spec files using a refinement, but this method doesn’t leak out into the rest of the codebase.
The compromise is that you must write using RSpec at the top of every file, which I don’t think is a large price to pay. But, you might disagree and we’ll get to that shortly.
RSpec isn’t the only DSL that’s commonly used, and you might not even have thought of it as a DSL – after all, it’s just Ruby. You can also view the routes file in a Rails application as a DSL of sorts, or the query methods ActiveRecord provides. In fact, the Sequel gem actually does, optionally, let you write queries more fluently by using a refinement to add methods and behaviour to strings, symbols and other objects.
DSLs are everywhere, and refinements can help make them even more expressive without resorting to monkey-patching or other brittle techniques.
Onto my last example.
Internal access control
Refinements might not just be useful for safer monkey-patching or implementing DSLs.
We might also be able to harness refinements as a design pattern of sorts, and use them to ensure that certain methods are only callable from specific, potentially-restricted parts of our codebase.
For example, consider a Rails application with a model that has some “dangerous” or “expensive” method.
moduleUserAdminrefineUserdodefpurge!user.associated_records.delete_all!user.delete!endendend# in app/controllers/admin_controller.rbclassAdminController<ApplicationControllerusingUserAdmindefpurge_userUser.find(params[:id]).purge!endend
By using a refinement, the only places we can call this method are where we’ve explicitly activated that refinement.
From everywhere else – normal controllers, views or other classes – even though they might be handling the same object – the very same instance, even – the dangerous or expensive method is guaranteed not to be available there.
I think this is a really interesting use for refinements – as a design pattern rather than just a solution for monkey-patching – and while I know there could be some obvious objections to that suggestion, I’m certainly curious to explore it a bit more before I decide it’s not worthwhile.
So those are some examples of things we might be able to do with refinements. I think they are all potentially very interesting, and potentially useful.
So, finally, to the question I’m curious about. If refinements can do all of these things in such elegant and safe ways, why aren’t we seeing more use of them?
Why is nobody using refinements
It’s been five years since they appeared, and almost three years since they were officially a part of Ruby. And yet, when I search GitHub, almost none of the results are actual uses of refinements.
In fact, some of the top hits are gems that actually try to “remove” refinements from the language!
You can see in the description: “No one knows what problem they solve or how they work.”! Well, hopefully we at least have some ideas about that now.
I actually asked another of the speakers at RubyConf2015 — who will remain nameless — what they thought the answer to my question might be, and they said:
So I don’t find this answer very satisfying. Why are they bad?
I asked them why, and they replied
“Because they’re just another form of monkey patching, right?”
Well – yes, sort of, but also… not really.
And just because they might be related in some way to monkey-patching – does that automatically make them bad, or not worth understanding?
I can’t shake the feeling that this is the same mode of thinking that leads to ideas like “meta-programming is too much magic” or “using single or double quoted strings consistently is a *very important thing*” or that something – anything – you type into a text editor can be described as “awesome” when that word should be reserved exclusively for moments in your life like seeing the Grand Canyon for the first time, and not when you install the latest gem or anything like that.
I am… suspicious… of “awesome”, and so I’m also suspicious of “bad”.
I asked another friend if they had any ideas about why people weren’t using refinements, and they said “because they’re slow”, again, as if it was a fact.
““TL;DR: Refinements aren’t slow. Or at least they don’t seem to be slower than ‘normal’ methods”
So why aren’t people using refinements? Why do people have these ideas that they are slow, or just plain bad?
Is there any solid basis for those opinions?
As I told you right at the start, I don’t have a neatly packaged answer, and maybe nobody does, but here are my best guesses, based on tangible evidence and understanding of how refinements actually work
1. Lack of understanding?
While refinements have been around for almost five years, the refinements you see now are not the same as those that were introduced half a decade ago. Originally, they weren’t strictly lexically scoped, and while this provides some opportunity for more elegant code than what we’ve seen today – think not having to writing using at the top of every RSpec file, for example – it also breaks the guarantee that refinements cannot affect distant parts of a codebase.
It’s also probably true that lexical scope is not a familiar concept for many Ruby developers. I’m not ashamed to say that even though I’ve been using Ruby for over 13 years, it’s only recently that I really understood what lexical scope is actually about. I think you can probably make a lot of money writing Rails applications without ever really caring about lexical scope, and yet, without understanding it, refinements will always seem like confusing and uncontrollable magic.
The evolution of refinements hasn’t been smooth, and I think that’s why some people might feel like “nobody knows how they work or what problem they solve”. It doesn’t help, for example, that a lot of the blog posts you’ll find when you search for “refinements” are no longer accurate.
Even the official Ruby documentation is actually wrong!
This hasn’t been true since Ruby 2.1, I think, but this is what the documentation says right now. Nudge to the ruby-core team: issue 11681 might fix this…
UPDATE: since giving the presentation, this patch has been merged!
I think some of this … “information rot” can explain a little about why refinements stayed in the background.
There were genuine and valid questions about early implementation and design choices, and I think it’s fair to say that some of the excitement about this new feature of Ruby was dampened as a result. But even with all the outdated blog posts, I don’t think this entirely explains why nobody seems to be using them.
So perhaps it’s the current implementation that people don’t like.
2. Adding using everywhere is a giant pain in the ass?
Maybe the idea of having to write using everywhere goes against the mantra of DRY - don’t repeat yourself - that we’ve generally adopted as a community. After all, who wants to have to remember to write using RSpec or using Sequel or using ActiveSupport at the top of almost every file?
It doesn’t sound fun.
And this points a another potential reason:
3. Rails (and Ruby) doesn’t use them
A huge number of Ruby developers spend most if not all of their time using Rails, and so Rails has a huge amount of influence over which language features are promoted and adopted by the community.
$ fgrep 'refine ' -R rails | wc -l # => 0
Rails contains perhaps the largest collection of monkey-patches ever, in the form of ActiveSupport, but because it doesn’t use refinements, no signal is sent to developers that we should – or even could – be using them.
Now: You might be starting to form the impression that I don’t like Rails, but I’m actually very hesitant to single it out. To be clear: I love Rails – Rails feeds and clothes me, and enables me to fly to Texas and meet all y’all wonderful people. The developers who contribute to Rails are also wonderful human beings who deserve a lot of thanks.
I also think it’s easily possible, and perhaps even likely, that there’s just no way for Rails to use refinements as they exist right now to implement something at the scale of ActiveSupport. It’s possible.
But even more than this, nothing in the Ruby standard library itself uses refinements!
You can also get into some really weird situations if you try to include a module into a refinement, where methods from that module cannot find other methods define in the same module.
But this doesn’t necessarily mean that refinements are broken; all of these are either by design, or a direct consequence of lexical scoping.
Even so, they are unintuitive and it could be that aspects like these are a factor limiting the ability to use refinements at the scale of, say, ActiveSupport.
5. Refinements solve a problem that nobody has?
As easy as it is for me to stand up here and make a logical and rational argument about why monkey-patching is bad, and wrong, and breaks things, it’s impossible to deny that fact that even since you started reading this page, software written using libraries that rely heavily on monkey-patching has made literally millions of dollars.
So maybe refinements solve a problem that nobody actually has. Maybe, for all the potential problems that monkey patching might bring, the solutions we already have for managing those problems – test suites, for example – are already doing a good enough job at protecting us.
But even if you disagree with that – which I wouldn’t blame you for doing – perhaps it points at another reason that’s more compelling. Maybe refinements aren’t the right solution for the problem of monkey-patching. Maybe the right solution is actually something like: object-oriented design.
6. The rise of Object-oriented design
I think it’s fair to say that over the last two or three years, there’s been a significant increase in interest within the Ruby community in “Object Oriented Design”, which you can see in the presentations that Sandi Metz, for example, has given, or in her book, or discussion of patterns like “Hexagonal Architectures”, and “Interactors”, and “Presenters” and so on.
The benefits that O-O design tends to bring to software are important and valuable – smaller objects with clearer responsibilities, that are easier and faster to test and change – all of this helps us do our jobs more effectively, and anything which does that must be good.
And, from our perspective here, there’s nothing you can do with refinements that cannot also be accomplished by introducing one or more new objects or methods to encapsulate the new or changed behaviour.
For example, rather than adding a “shout” method to all Strings, we could introduce a new class that only knows about shouting, and wrap any strings we want shouted in instances of this new class.
I don’t want to discuss whether or not this is actually better than the refinement version, partly because this is a trivial example, so it wouldn’t be realistic to use, but mostly because I think there’s a more interesting point.
While good O-O design brings a lot of tangible benefits to software development, the cost of “proper O-O design” is verbosity; just as a DSL tries to hide the act of programming behind language that appears natural, the introduction of many objects can – sometimes – make it harder to quickly grasp what the overall intention of code might be.
And the right balance of explicitness and expressiveness will be different for different teams, or for different projects. Not everyone who interacts with software is a developer, let alone someone trained in software design, and so not everybody can be expected to easily adopt sophisticated principles with ease.
Software is for its users and sometimes the cost of making them deal with extra objects or methods might not be worth the benefit in terms of design purity. It is – like so many things – often subjective.
To be clear – I’m not in any way arguing that O-O design is not good; I’m simply wondering, whether or not it being good necessarily means that other approaches should not be considered in some situations.
So what’s the right answer?
And those are the six reasonable reasons that I could come up with as to why nobody is using refinements. So which is the right answer? I don’t know. There’s probably no way to know.
I think these are all potentially good, defensible reasons why we might have decided collectively to ignore Refinements.
However… I am not sure any of them are the answer that most accurately reflects reality. Unfortunately, I think the answer is more likely to the first one we encountered on this journey:
Because other people told us they are “bad”.
Let me make a confession.
When I said “this is not a sales pitch for refinements”, I really meant it. I’m fully open to the possibility that it might never be a good idea to use them. I think it’s unlikely, but it’s certainly possible.
And to be honest, it doesn’t really bother me either way!
What I do care about, though, is that we might start to accept and adopt opinions like “that feature is bad”, or “this sucks”, without ever pausing to question them or explore the feature ourselves.
Sharing opinions is good. Nobody has the time the research everything. That would not only be unrealistic, but one of the benefits of being in a community is that we can benefit from each other’s experiences. We can use our collective experience to learn and improve. This is definitely a good thing.
But if we just accept opinions as facts, without even asking “why”… I think this is more dangerous. If nobody ever questioned an opinion as fact, then we’d still believe the world was flat!
It’s only by questioning opinions that we make new discoveries, and that we learn for ourselves, and that — together — we make progress as a community.
The “sucks”/“awesome” binary can be an easy and tempting shorthand, and it’s even fun to use – but it’s an illusion. Nothing is ever that clear cut.
There’s a great quote by a British journalist and doctor called Ben Goldacre, that he uses any time someone tries to present a situation as starkly either good or bad:
“I think you’ll find it’s a bit more complicated that that.”
This is how I feel whenever anyone tells me something “sucks”, or is “awesome”. It might suck for you, but unless you can explain to me why it sucks, then how can I decide how your experience might apply to mine?
One person’s “suck” can easily be another person’s “awesome”, and they are not mutually exclusive. It’s up to us to listen and read critically, and then explore for ourselves what we think.
And I think this is particularly true when it comes to software development.
Explore for yourselves
If we hand most, if not all responsibility for that exploration to the relatively small number of people who talk at conferences, or have popular blogs, or who tweet a lot, or who maintain these very popular projects and frameworks, then that’s only a very limited perspective compared to the enormous size of the Ruby community.
I think we have a responsibility not only to ourselves, but also to each other, to our community, not to use Ruby only in the ways that are either implicitly or explicitly promoted to us, but to explore the fringes, and wrestle with new and experimental features and techniques, so that as many different perspectives as possible inform on the question of “is this good or not”.
If you’ll forgive the pun, there are no constants in programming – the opinions that Rails enshrines, even for great benefit, will change, and even the principles of O-O design are only principles, not immutable laws that should be blindly followed for the rest of time. There will be other ways of doing things. Change is inevitable.
So we’re at the end now. I might not have been able to tell you precisely why so few people seem to be using refinements, but I do have one small request.
Please – make a little time to explore Ruby. Maybe you’ll discover something simple, or maybe something wonderful. And if you do, I hope you’ll share it with everyone.
Certainly, the QR code is convenient, but it’s also opaque and if you just scan it and move on with the setup, you’ll never see it again
This bit me when I had my phone replaced earlier this year, only two find that my authenticator app hadn’t stored any backup of the credentials it needed to generate tokens1. I tried to log in to one of my newly-secured 2FA services (Gandi, in this case), only to find myself unable to generate the security code, and without any way to log in, no way to re-add the credentials to the authenticator app on the phone!
In this specific case I managed to get back in by calling the service and verifying my identity over the phone, but it’s hassle that I’d rather not repeat.
So, to avoid this, here’s what I do now.
Get the secret key
Instead of scanning the QR code, ask for the secret key:
Take a note of this somewhere (I store it in [1Password], for example). Now if you ever need to somehow re-add this service to your authenticator application, you can use this code.
Once you have the code, you’re free to switch back to the QR code (which contains exactly the same information) for a more convenient way of getting that data into your app.
2FA using the command-line
One secondary benefit of having the code available is that you don’t need to pull out your phone to generate an authentication code. Using a program called oathtool, you can generate codes in the Terminal:
You can even get the code ready to paste straight into the web form:
$ oathtool --base32 --totp "YOURCODE..." | pbcopy
Now you can simply switch back to the webpage and paste the code using a single keystroke. Boom.
Sharing 2FA with colleagues
Another benefit of storing the code is that you can give other people access to the same credentials without everyone needing to be present when the QR code is on the screen (which might not even be possible if they are remote).
Instead, you just need to (securely) share the code with them, and they can add it to their authenticator app and start accessing the 2FA-secured service. Admittedly, this is more cumbersome than scanning a convenient QR code
This may have been because I didn’t encrypt the backup for my phone in iTunes, which means (I believe) that no passwords will be saved, but it could also be a legitimate security measure by the app itself.↩
If you haven’t seen the video, then I’d still strongly encourage you to read the blog post. While I can now see the inspiration for wanting to discuss these ideas1, the post really does stand on it’s own.
I’m not going to re-explain it here, so yes, really, go read it now.
What I found really interesting here was the idea of building new enumerators by re-combining existing enumerators. I’ll use a different example, one that is perhaps a bit too simplistic (there are more concise ways of doing this in Ruby), but hopefully it will illustrate the point.
Let’s imagine you have an Enumerator which enumerates the numbers from 1 up to 10:
You can now use that to do all sorts of enumerable things like mapping, selecting, injecting and so on. But you can also build new enumerables using it. Say, for example, we now only want to iterate over the odd numbers between 1 and 10.
We can build a new Enumerator that re-uses our existing one:
> odd_numbers = Enumerator.new do |yielder|
numbers.each do |number|
yielder.yield number if number.odd?
=> #<Enumerator: #<Enumerator::Generator:0x007fc0b38de6b0>:each>
So, that’s quite neat (albeit somewhat convoluted compared to 1.upto(10).select(&:odd)). To extend this further, let’s imagine that I hate the lucky number 7, so I also don’t want that be included. In fact, somewhat perversely, I want to stick it right in the face of superstition by replacing 7 with the unluckiest number, 13.
Yes, I know this is weird, but bear with me. If you have read Tom’s post (go read it), you’ll already know that this can also be achieved with a new enumerator:
> odd_numbers_that_arent_lucky = Enumerator.new do |yielder|
odd_numbers.each do |odd_number|
if number == 7
=> #<Enumerator: #<Enumerator::Generator:0x007fc0b38de6b0>:each>
StopIteration: iteration reached an end
In Tom’s post he shows how this works, and how you can further compose enumerators to to produce new enumerations with specific elements inserted at specific points, or elements removed, or even transformed, and so on.
A hidden history of enumerable transformations
What I find really interesting here is that somewhere in our odd_numbers enumerator, all the numbers still exist. We haven’t actually thrown anything away permanently; the numbers we don’t want just don’t appear while we are enumerating.
The enumerator odd_numbers_that_arent_lucky still contains (in a sense) all of the numbers between 1 and 10, and so in the tree composition example in Tom’s post, all the trees he creates with new nodes, or with nodes removed, still contain (in a sense) all those nodes.
It’s almost as if the history of the tree’s structure is encoded within the nesting of Enumerator instances, or as if those blocks passed to Enumerator.new act as a runnable description of the transformations to get from the original tree to the tree we have now, invoked each time any new tree’s children are enumerated over.
I think that’s pretty interesting.
Notes on ‘Catamorphisms’
In the section on Catamorphisms (go read it now!), Tom goes on to show that recognising similarities in some methods points at a further abstraction that can be made – the fold – which opens up new possibilities when working with different kinds of structures.
What’s interesting to me here isn’t anything about the code, but about the ability to recognise patterns and then exploit them. I am very jealous of Tom, because he’s not only very good at doing this, but also very good at explaining the ideas to others.
Academic vs pragmatic programming
This touches on the tension between the ‘academic’ and ‘pragmatic’ nature of working with software. This is something that comes up time and time again in our little sphere:
Now I’m not going to argue that anyone working in software development should have a degree in Computer Science. I’m pretty sympathetic with the idea that many “Computer Science” degrees don’t actually bear much of direct resemblance to the kinds of work that most software developers do2.
Ways to think
What I think university study provides, more than anything else, is exposure and training in ways to think that aren’t obvious or immediately accessible via our direct experience of the world. Many areas of study provide this, including those outside of what you might consider “science”. Learning a language can be learning a new way to think. Learning to interpret art, or poems, or history is learning a new way to think too.
Learning and internalising those ways to think give perspectives on problems that can yield insights and new approaches, and I propose that that, more than any other thing, is the hallmark of a good software developer.
Going back to the blog post which, as far I know, sparked the tweet storm about “programming and maths”, I’d like to highlight this section:
At most academic CS schools, the explicit intent is that students learn programming as a byproduct of learning CS. Programming itself is seen as rather pedestrian, a sort of exercise left to the reader.
For actual developer jobs, by contrast, the two main skills you need these days are programming and communication. So while CS still does have strong ties to math, the ties between CS and programming are more tenuous. You might be able to say that math skills are required for computer science success, but you can’t necessarily say that they’re required for developer success.
What a good computer science (or maths or any other logic-focussed) education should teach you are ways to think that are specific to computation, algorithms and data manipulation, which then
provide the perspective to recognise patterns in problems and programs that are not obvious, or even easily intuited, and might otherwise be missed.
provide experience applying techniques to formulate solutions to those problems, and refactorings of those programs.
Plus, it’s fun to achieve that kind of insight into a problem. It’s the “a-ha!” moment that flips confusion and doubt into satisfaction and certainty. And these insights are also interesting in and of themselves, in the very same way that, say, study of art history or Shakespeare can be.
So, to be crystal clear, I’m not saying that you need this perspective to be a great programmer. I’m really not. You can build great software that both delights users and works elegantly underneath without any formal training. That is definitely true.
Back to that quote:
the ties between CS and programming are more tenuous … you can’t necessarily say that they’re required for developer success.
All I’m saying is this: the insights and perspectives gained by studying computer science are both useful and interesting. They can help you recognise existing, well-understood problems, and apply robust, well-understood and powerful solutions.
That’s the relevance of computer science to the work we do every day, and it would be a shame to forget that.
In the last 15 minutes or so of the video, the approach Tom uses to add a “child node” to a tree is interesting but there’s not a huge amount of time to explore some of the subtle benefits of that approach↩
Which is, and let’s be honest, a lot of “Get a record out of a database with an ORM, turn it into some strings, save it back into the database”.↩
I’ve been toying around with Docker for a while now, and I really like it.
The reason why I became interested, at first, was because I wanted to move the Printer server and processes to a different VPS which was already running some other software (including this blog), but I was a bit nervous about installing all of the dependencies.
What if something needed an upgraded version of LibXML, but upgrading for one application ended up breaking a different one? What if I got myself in a tangle with the different versions of Ruby that different applications expect?
If you’re anything like me, servers tend to be supremely magical things; I know enough of the right incantations to generally make the right things appear in the right places at the right time, but it’s so easy to utter the wrong spell and completely mess things up1.
Docker provides an elegant solution for these kinds of worries, because it allows you to isolate applications from each other, including as many of their dependencies as you like. Creating images with different versions of LibXML, or Ruby, or even PostgreSQL is almost trivially easy, and you can be confident that running any combination will not cause unexpected crashes and hard-to-trace software version issues.
However, while Docker is simple in principle, it’s not trivial to actually deploy with it, in the pragmatic sense.
What I mean is getting to a point where deploying a new application to your server is as simple (or close enough to it) as platforms like Heroku make it.
Now, to be clear, I don’t specifically mean using git push to deploy; what I mean is all the orchestration that needs to happen in order to move the right software image into the right place, stop existing containers, start updated containers and make sure that nothing explodes as you do that.
But, you might say, there are packages that already help with this! And you’re right. Here are a few:
Dokku, the original minimal Heroku clone for Docker
Orchard, a managed host for your Docker containers
…and many more. I don’t know if you’ve heard, but Docker is, like, everywhere right now.
So why not use one of those?
That’s a good question
Well, a couple of reasons. I think Orchard looks great, but I like using my own servers for software when I can. That’s just my personal preference, but there it stands, so tools like Orchard (or Octohost and so on) are not going to help me at the moment.
Dokku is good, but I’d like to have a little more control of how applications are deployed (as I said above, I don’t really care about git push to deploy, and even in Heroku it can lead to odd issues, particularly with migrations).
Flynn isn’t really for me, or at least I don’t think it is. It’s for dev-ops running apps on multiple machines, balancing loads and migrating datacenters; I’m running some fun little apps on my personal VPS. I’m not interested in using Docker to deploy across multiple, dynamically scaling nodes; I just want to take advantage of Docker’s isolation on my own, existing, all-by-its-lonesome VPS.
But, really more than anything, I wanted to understand what was happening when I deploy a new application, and be confident that it worked, so that I can more easily use (and debug if required) this new moving piece in my software stack.
So I’ve done some exploring
I took a bit of time out from building Harmonia to play around more with Docker, and I’d like to write about what I’ve found out, partly to help me make it concrete, and partly because I’m hoping that there are other people out there like me, who want some of the benefits that Docker can bring without necessarily having to learn so much about using it to it’s full potential in an ‘enterprise’ setting, and without having to abandon running their own VPS.
There ought to be a simple, easy path to getting up and running productively with Docker while still understanding everything that’s going on. I’d like to find and document it, for everyone’s benefit.
If that sounds like the kind of think you’re interested in, and would like reading about, please let me know. If I know there are enough people interested in the idea, then I’ll get to work.
It was really interesting to read the news about the potential cancellation of Wicked Good Ruby, a Ruby conference that was due to run for a second time in Boston this year:
Rather than half-ass a conference we’ve decided to cancel it. All tickets will be fully reimbursed. I’ve already reached out to those that have purchased with how that will be done.
There’s lots of interesting stuff in this thread, but one point that jumped out to me was the financial risk involved:
Zero sponsors. […] I had 2 companies inquire about sponsorship. One asked to sponsor but never paid the sponsorship invoice after 82 days.
Last year we lost $15k. In order to made it worth our effort this year we needed to make a profit. The conference we had in mind required a $50k sponsorship budget with an overall budget of close to $100k. (last year’s conference cost about $125k) Consider to date, after 6 months, we have received $0 in sponsorship the financial risk was too high.
Since announcing this a few hours ago I’ve been contacted by 3 other regional conference organizers. They are all having similar issues this year. Sponsorship is incredibly difficult to come by […] I didn’t get the sense they were going to bail but I think this is a larger issue than just Boston.
With costs in the tens or even hundreds of thousands of dollars, it’s really no wonder that organisers might have second thoughts.
But does running a conference really need to come with such an enormous financial risk? With Ruby Manor, our biggest budget was less than £2,000 (around $3,000). Here’s why:
We don’t use expensive ‘conference’ venues…
… so we aren’t locked into their expensive conference catering.
We run a single track for a single day, so we only need one main room.
We meticulously pare away other expenses like swag, t-shirts, catering and so on, where it’s clear that attendees are perfectly capable of handling those themselves.
Now, it could be that there are a few factors unique to Ruby Manor that make it difficult, or even impossible, for other conferences to follow the same model. For instance, holding the event in the center of a big city like London means there’s already a wide range of lunch options for attendees, right outside the venue.
Another problem could be the availability of university and community venues as an alternative to conference centres. I really don’t know if it’s possible or not to rent spaces like this in other cities or countries. A quick look at my local university indicates that it’s totally feasible to rent a 330-seat auditorium for less than $1,000, and even use their coffee/tea catering for a total of less than $3,0001, all-in.
I would be genuinely fascinated for other conferences to start publishing their costing breakdown. LessConf have already done this, and even though I might not have chosen to spend quite so much money on breakfasts and surprises, I genuinely do applaud their transparency for the benefit of the community.
I think I’ve come up with a plan: reduce the conference from 2 nights to 1 night. Cut out many of the thrills that I was planning. This effectively would reduce our operational costs from ~$100k to around ~$50k. This would also allow us to reduce the ticket prices (I would reimburse current tickets for the difference).
I genuinely wish the organisers the best of luck. It’s a tough gig, running a conference. That said, $50,000 is still an enormous amount of money, and I cannot help but feel that it’s still far higher than it needs to be.
Every hour you spend as a conference organiser worrying about sponsorships or ticket sales or other financial issues, is an hour that could be spent working on making the content and the community aspects as good as they can be.
Let’s not forget: the only really important part of a conference is getting a diverse group of people with shared interests into the same space for a day or two. Everything else is just decoration. A lot of the time, even the presentations are just decoration. It’s getting the community together that’s important.
I realise that many people expect, consciously or otherwise, some or all of the peripheral bells and whistles that surround the core conference experience. For some attendees, going to a conference might even be akin to a ‘vacation’ of sorts. Perhaps a conference without $15,000 parties feels like less of an event… less of an extravaganza.
But consider this: given the choice between a glitzy, dazzling extravaganza, and a solid conference that an organiser can run without paralysing fear of financial ruin, I know which I would choose, and I know which ultimately benefits the community more.
To be clear, they require that you cannot make a profit on events they host, but from what I can tell, most conference organisers don’t wish to run their events for profit anyway.↩
This irregularly-kept blog thing is getting so irregular it’s almost regularly irregular. Lest you fool yourself into presuming any ability to predict when I might write here (i.e. “never”), let me thwart your erroneous notion by serving up this rambling missive, unexpected and with neither warning nor warrant!
Take that, predictability! Chalk another one up for chaos.
I live in Austin now, or at least, for the moment
In summer last year, I moved from London to Austin. My other half had already been living in Austin for two years, and I’d been flying back and forth ever other month or so, but 24 months of that is quite enough, and so I bit the bullet and gave up my London life for Stetsons and spurs. It’s been quite an adventure so far, although I do miss a few people back in the UK.
We don’t know how long we’ll stay here, but for the moment I’m enjoying the weather immensely. You might think it’s a balmy summer in London when temperatures regularly reach 20°C, but during Austin’s summer the temperature never drops below 20°C, even at night. Can you even conceive of that?
Farewell Go Free Range, Howdy Exciting
Leaving the UK also marked the beginning of the end of the Free Range experiment for me. Ever since I started kicking the ideas for Free Range around in late 2008, I’ve always considered it an experiment. I wish I could say that it had been a complete success, and it was very successful in a number of important ways, but if you’re going to pour a lot of energy into trying to make something for yourself, it really needs to be moving in the direction you feel is valuable.
I wrote a bit more on the Exciting blog and the Free Range blog. I could say a lot more about this – indeed, I have actually written thousands of words in various notepads and emails – but I’ll save that for another time; my memoirs, maybe. It takes a lot of effort to keep a small boat moving in a consistent direction through uncharted waters.
So: I’ve spun out a lot of the projects I was driving into a new umbrella-thing called Exciting, and that’s the place to look at what I’m working on.
Right, but what are you doing exactly?
Well, I’m still doing the occasional bit of client work, and I have some tinkering projects that I need to write more about, but this year I have mostly been1 working on Harmonia. Although it was built for Free Range, it’s still both a product and a way of working that I believe lots of teams and companies could benefit from. I’ve added a fair bit of new functionality (more calendar and email functionality, webhooks, Trello integration) and fixed a whole bunch of usability issues, the lion’s share of the work has been in working on how to communicate what Harmonia does and how it could help you.
I really cannot overstate the amount of effort this takes. It’s exhausting. Some of that is doubtless because I am a developer by training, and so I’ve had to learn about design, user experience, copywriting, marketing, and everything in between, as I’ve gone along. It’s very much out of my comfort zone, but it’s simply not possible to succeed without them.
You can build the best product in the world, but if nobody knows it exists or can quickly determine whether or not it’s something they want… nobody will ever use it.
The good thing is that the web is the ideal medium for learning all this, because you can do it iteratively. I’ve completely redesigned the Harmonia landing page four times since it became my main focus, and each time I think it’s getting better at showcasing the benefits that it can bring. It still needs more work though; as of writing this I think it’s too wordy and the next revision will pare it down quite a bit.
It’s also a real challenge to keep up this effort while working alone. It’s hard to wrangle a group of people into a coherent effort, but once you succeed then it provides a great support structure to keep motivation up and to share and develop ideas. Working by yourself, you don’t have to deal with anyone else’s whims or quirks, but you also only have yourself to provide motivation and reassurance that what you’re working on is valuable, and that the decisions you’re making are sensible.
What’s more, it can be challenging to stay motivated in a world seemingly filled by super-confident, naturally-outgoing entrepreneur types. I don’t operate with the unshakable confidence that what I’m building is awesome, or that my ideas are worth sharing, or that they even have any particular merit.
Confronted with the boundless crowd of what-seems-typical Type A entrepreneurs that fill the internet, prolifically, with their reckons and newsletters and podcasts and blog posts and courses, I often wonder about the simpler life of just being an employee; of offloading the risk onto someone else in exchange for a steady incoming and significantly less crippling self-doubt. I’m sure I’m not the only person who feels this way, whose skin crawls as yet another service or blog post or person is cheaply ascribed the accolade “awesome” when really, I mean, really, is it? Is it?
But… there’s still a chance that I might be able to carve out a niche of my own, and maybe build another little company of friends working on small things that we care about, and investing in our future collectively. I still believe that’s a possibility.
Anyway, so that’s what I’m doing, mostly: crushing it2.
Couldn’t resist this turn of phrase; see here if you don’t immediately get it.↩
I’ve been trying to largely ignore the recent TDD discussion prompted by DHH’s RailsConf 2014 keynote. I think I can understand where he’s coming from, and my only real concern with not sharing his point of view is that it makes it less likely that Rails will be steered in a direction which makes TDD easier. But that’s OK, and if my concern grows then I have opportunities to propose improvements.
I don’t even really mind what he’s said in his latest post about unit testing the Basecamp codebase. There are a lot of Rails applications – including ones I’ve written – where a four-minute test suite would’ve been a huge triumph.
I could make some pithy argument like:
… but let’s be honest, four minutes for a substantial and mature codebase is pretty excellent in the Rails world.
So that is actually pretty cool.
A lot of that speed is no doubt because Basecamp is using fixtures: test data that is loaded once at the start of the test run, and then reset by wrapping each test in a transaction and rolling back before starting the next test.
This can be a benefit because the alternative – assuming that you want to get some data into the database before your test runs – is to insert all the data required for each test, which can potentially involve a large tree of related models. Doing this hundreds or thousands of times will definitely slow your test suite down.
(Note that for the purposes of my point below, I’m deliberately not considering the option of not hitting the database at all. In reality, that’s what I’d do, but let’s just imagine that it wasn’t an option for a second, yeah? OK, great.)
So, fixtures will probably make the whole test suite faster. Sounds good, right?
The problem with fixtures
I feel like this is glossing over the real problem with fixtures: unless you are using independent fixtures for each test, your shared fixtures have coupled your tests together. Since I’m pretty sure that nobody is actually using independent fixtures for every test, I am going to go out on a limb and just state:
Fixtures have coupled your tests together.
This isn’t a new insight. This is pain that I’ve felt acutely in the past, and was my primary motivation for leaving fixtures behind.
Say you use the same ‘user’ fixture between two tests in different parts of your test suite. Modifying that fixture to respond to a change in one test can now potentially cause the other test to fail, if the assumptions either test was making about its test data are no longer true (e.g. the user should not be admin, the user should only have a short bio, or so on).
If you use fixtures and share them between tests, you’re putting the burden of managing this coupling on yourself or the rest of your development team.
Going back to DHH’s post:
Why on earth would you run your entire test harness for every single line change in a particular model? If you have so little confidence in the locality of your changes, the tests are indeed telling you that the system has overly high coupling.
What fixtures do is introduce overly high coupling in the test suite itself. If you make any change to your fixtures, I do not think it’s possible to be confident that you haven’t broken a single test unless you run the whole suite again.
Fixtures separate the reason test data is like it is from the tests themselves, rather than keeping them close together.
I might be wrong
Now perhaps I have only been exposed to problematic fixtures, and there are techniques for reliably avoiding this coupling or at least managing it better. If that’s the case, then I’d really love to hear more about them.
Or, perhaps the pain of managing fixture coupling is objectively less than the pain of changing the way you write software to both avoid fixtures AND avoid slowing down the test suite by inserting data into the database thousands of times?
I’ve been playing with Raygun.io over the last day or so. It’s a tool, like Honeybadger, Airbrake or Errbit, for managing exceptions from other web or mobile applications. It will email you when exceptions occur, collapse duplicate errors together, and allows a team to comment and resolve exceptions from their nicely designed web interface.
I’ve come to the conclusion that integrating with something like this is basically a minimum requirement for software these days. Previously we might’ve suggested an ‘iterative’ approach of emailing exceptions directly from the application before later using one of these services, but I no longer see the benefit in postponing a better solution when it’s far simpler to work with one of these services than it is to set up email reliably.
It seems pretty trivial to integrate with a Rails application – just run a generator to create the initializer complete with API key. However, I had to do a bit more work to hook it into a Rack application (which is what Vanilla is). In my config.ru:
# Setup Raygun and add it to the middlewarerequire'raygun'Raygun.setupdo|config|config.api_key=ENV["RAYGUN_API_KEY"]enduseRaygun::RackExceptionInterceptor# Raygun will re-raise the exception, so catch it with something elseuseRack::ShowExceptions
The documentation for this is available on the Raygun.io site, but at the moment the actual documentation link on their site points to a gem, which more-confusingly isn’t actually the gem that you will have installed. Reading the documentation in the gem README also reveals how to integrate with Resque, to catch exceptions in background jobs.
One thing that’s always worth checking when integrating with exception reporting services is whether or not they support SSL, and thankfully it looks like that’s the default (and indeed only option) here.
The Raygun server also sports a few plugins (slightly hidden under ‘Application Settings’) for logging exception data to HipChat, Campfire and the like. I’d like to see a generic webhook plugin supported, so that I could integrate exception notification into other tools that I write; thankfully that’s the number one feature request at the moment.
My other request would be that the gem should try not to depend on activesupport if possible. I realise for usage within Rails, this is a non-issue, but for non-Rails applications, loading ActiveSupport can introduce a number of other gems that bloat the running Ruby process. As far as I can tell, the only methods from ActiveSupport that are used are Hash#blank? (which is effectively the same as Hash#empty?) and String#starts_with? (which is just an alias for the Ruby-default String#start_with?). Pull request submitted.
I was lucky enough to be gifted a Twine by my colleagues at Go Free Range last weekend, and I took the opportunity to put together a very simple service that demonstrates how it can be used.
If you haven’t heard of Twine, it’s a hardware and software platform for connecting simple sensors to the internet, and it makes it really very easy to do some fun things bridging the physical and online worlds.
On the hardware side, there’s a simple 2.7” square that acts as a bridge between your home WiFi network and a set of sensors.
Some of the sensors are built in to the square itself: temperature, orientation and vibration can be detected without plugging anything else in. You can also get external sensors, which connect to the square via a simple 3.5mm jack cable. If you buy the full sensor package, you’ll get a magnetic switch sensor, a water sensor and a ‘breakout board’ that lets you connect any other circuit (like a doorbell, photoresistor, button and so on) to the Twine.
Connecting the Twine to a WiFi network is elegant and features a lovely twist: you flip the Twine on its “back”, like a turtle, and it makes its own WiFi network available.
Connect to this from your computer, and you can then give the Twine the necessary credentials to log on to your home network, and once you’re done, flip it back onto its “belly” again and it will be ready to use. I really loved this simple, physical interaction.
On the software side, Twine runs an online service that lets you define and store ‘rules’ to your connected Twine units. These rules take the form of when <X> then <Y>, in a similar style to If This Then That. So, with a rule like when <vibration stops> then <send an email to my phone>, you could pop the Twine on top of your washing machine and be alerted when it had finished the final spin cycle.
As well as emailing, the Twine can flash it’s LED, tweet, send you an SMS, call you, or ping a URL via GET or POST requests including some of the sensor information.
Supermechanical, the company that launched Twine about a year and a half ago via Kickstarter, maintains a great blog with lots of example ideas of things that can be done.
All technology tends towards cat…
Just as the internet has found its singular purpose as the most efficient conduit for the sharing of cat pictures, so will the Internet of Things realise its destiny by becoming entirely focussed on physical cats, in all their unpredictable, scampish glory.
It’s neat having something in your house tweet or send you an email, but I like making software so I decided to explore building a simple server than the Twine could interact with, and thus, “Pinky Status” was born:
What follows is a quick explanation of how easy it was.
I hooked up the magnetic switch sensor to the Twine, and then used masking tape to secure the sensor to the side of the catflap, and then the magnet to the flap itself.
That way, when “Pinky” (that’s our cat) opened the flap, the magnet moves away from the switch sensor and it enters the ‘open’ state. It’s not pretty, but it works.
Next, we need a simple rule so that the Twine knows what to do when the sensor changes:
When the sensor changes to open, two things happen. Firstly, I get an email, which I really only use for debugging and I should probably turn it off, except that it’s pretty fun to have that subject line appear on my phone when I’m out of the house.
Secondly and far more usefully, the Twine pings the URL of a very, very simple server that I wrote.
The key part is at the very bottom – as Twine makes a POST request, the server simply creates another Event record with an alternating status (‘in’ or ‘out’), and then some logic in the view (not shown) can tell us whether or not the cat is in or out of the house.
In more recent versions of the code I’ve moved to Rails because it’s more familiar, but also slightly easier to do things like defend against duplicate events (normally when the cat changes her mind about going outside when her head is already through the flap) and other peripheral things.
But don’t be dissuaded by Rails - it really was as trivial as the short script above , showing some novel information derived from the simple sensor attached to the Twine. Deploying a server is also very easy thanks to tools like Heroku.
A few hours idle work and the secret life of our cat is now a little bit less mysterious than it was. I really enjoyed how quick and painless the Twine was to setup, and I can highly recommend it if you’re perhaps not comfortable enough to dive into deep sea of Arduinos, soldering and programming in C, but would still like to paddle in the shallower waters of the “internet of things”.
What happens when RSpec runs, or, what I think about testing with blocks
require"rspec/autorun"describe"an object"dobefore:alldo@shared_thing=Object.newendbefore:eachdo@something=Object.newendit"should be an Object"email@example.com_an(Object)enddescribe"compared to another object"dobefore:eachdo@other=Object.newendit"should not be equal"firstname.lastname@example.org_not==@otherendendafterdo@something=nilendend
This is obviously extremely dull and pointless – just like the minitest one – but it contains just enough to exercise the major parts of RSpec that I care about. It’s actually slightly more sophisticated than the example that I used for MiniTest, because RSpec provides a couple of notable features that MiniTest doesn’t provide. Specifically, these are before :all setup blocks, and nested groups of tests12.
I’m not particularly interested in looking at the other obvious distinguishing features of RSpec, like matchers and the BDD-style “should” language, as these aren’t actually a part of the core RSpec implementation3.
The two hallmark attributes here that I am interested in are:
grouping test definitions within blocks (as opposed to classes)
defining test behaviour using blocks (as opposed to methods)
Running the test spec
The simplest way of running this spec would be to save as something_spec.rb and run it from the command-line.
$ ruby something_spec.rb
Finished in 0.00198 seconds
2 examples, 0 failures
[Finished in 0.5s]
So – what’s actually happening here?
As with the minitest example, the first line loads a special file within the test library that not only loads the library, but also installs an at_exit hook for Ruby to run when the interpreter exists.
In RSpec’s case, this is defined in RSpec::Core::Runner.autorun. This calls RSpec::Core::Runner.run with ARGV and the stderr and stdout streams.
In contrast with MiniTest, RSpec parses the options at this point, and will try to determine whether or not to launch using DRb. In most cases it will create an instance of RSpec::Core::CommandLine with the parsed options, and then calls run on that instance.
Within the run method, some setup happens (mostly preamble to be output by the reporter, which is set via the configuration). Then we iterate through all of the “example groups”, returned by RSpec::world.example_groups4.
Let’s take a diversion to see how things actually get intoRSpec::world.example_groups.
Your example groups
Consider our example spec again. At the top we have a call to describe:
The describe method is actually defined within the module RSpec::Code::DSL, but this module is extended into self at the top level of the running Ruby interpreter (which is main, a singleton instance of Object), making the methods in that module available to call in your spec files. You can actually see all of the modules that have been extended into this instance:
From this we can tell that the ancestors of Object are still just Kernel and BasicObject, but the ancestors of the specific instancemain includes a few extra modules from RSpec. Anyway, moving on…
describe and RSpec::Core::ExampleGroup
The describe method in RSpec::Core::DSL passes its arguments straight through to RSpec::Core::ExampleGroup.describe. This is where things get a little interesting. Within this inner describe method, a subclass of RSpec::Code::ExampleGroup is created, and given a generated name.
describe"a thing"do# your tests, um, I mean specsendRSpec::Core::ExampleGroup.constants# => [:Nested_1, :Extensions, :Pretty, :BuiltIn, :DSL, :OperatorMatcher, :Configuration]
The class that was created is there: Nested_1. For each describe at the top level, you’ll have a new generated class:
describe"a thing"do# your specsenddescribe"another thing"do# more specsendRSpec::Core::ExampleGroup.constants# => [:Nested_1, :Nested_2, :Extensions, :Pretty, :BuiltIn, :DSL, :OperatorMatcher, :Configuration]
After each subclass is created, it is “set up” via the set_it_up method, which roughly speaking adds a set of metadata about the group (such as which file and line it was defined upon, and perhaps some information about the class if it was called in the form describe SomeClass do ...), and stashes that within the created subclass.
More importantly, however, the block which was passed to describe is evaluated against this new subclass using module_eval.
The effect of using module_eval against a class is that the contents of the passed block are evaluated essentially as if they were within the definition of that class itself:
classLionel;endLionel.module_evaldodefhello?"is it me you're looking for?"endendLionel.new.hello?# => "is it me you're looking for?"
You can see above that the behaviour is effectively the same as if we’d defined the hello? method within the Lionel class without any “metaprogramming magic”5.
It’s because of module_eval that you can define methods within example groups:
describe"a thing"dodefinvert_phase_polarity# waggle the flux capacitor or somethingendendRSpec::Core::ExampleGroup::Nested_1.instance_methods(false)# false means don't include methods from ancestors# => [:invert_phase_polarity]
These methods are then effectively defined as part of the Nested_1 class that we are implicitly creating. This means that methods defined in this way can be called from within your specs:
describe"a method in an example group"dodefthe_method_in_question:resultendit"can be called from within a spec"dothe_method_in_question.should==:resultendend
We’ll see how this actually works a bit later. Knowing that the contents of the describe block are effectively evaluated within a class definition also explains what’s happening when the before methods are called:
Because this is evaluated as if it was written in a class definition, then before must be a method available on the ExampleGroup class. And indeed it is – RSpec::Code::ExampleGroup.before.
The before method actually comes from the module RSpec::Core::Hooks, which is extended into ExampleGroup. RSpec has a very complicated behind-the-scenes hook registry, which for the purposes of brevity I’m not going to inspect here..
The before method registers its block within that registry, to be retrieved later when the specs actually run.
Because I’m not going to really look too deeply at hooks, the call to the after method works in pretty much the same way. Here it is though, just because:
The spec itself
The next method that’s module_eval‘d within our ExampleGroup subclass is the it:
it"should be an Object"email@example.com_an(Object)end
Users of RSpec will know that you can call a number of methods to define a single spec: it, specifyexample, and others with additional meaning like pending or focus. These methods are actually all generated while RSpec is being loaded, by calls to define_example_method within the class definition of ExampleGroup. For simplicity’s sake (pending and focussed specs are somewhat outwith the remit of this exploration), we’ll only look at the simplest case.
When it is called, more metadata is assembled about the spec (again, including the line and file), and then both this metadata and the block are passed to RSpec::Core::Example.new, which stashes them for later.
Within our outer example group, we’ve nested another group:
describe"compared to another object"dobefore:eachdo@other=Object.newendit"should not be equal"firstname.lastname@example.org_not==@otherendend
Just as the top-level call to describe invokes a class method on RSpec::Core::ExampleGroup, this call will be invoked against the subclass of ExampleGroup (i.e. Nested_1) that our outer group defined. Accordingly, each call to describe defines a new subclass6, stored as a constant within the top-level class: Nested_1::Nested_1. This subclass is stored within an array of children in the outer Nested_1 class.
Within the definition, our before and it calls evaluate as before.
Your spec, as objects
So, for every describe, a new subclass of ExampleGroup is created, with calls to before and after registering hooks within that subclass, and then each it call defines a new instance of RSpec::Core::Example, and these are stored in an array called examples within that subclass.
We can even take a look at these now, for a simplified example:
Where example groups are nested, further subclasses are created, and stored in an array of children within their respective parent groups.
Phew. The detour we took when looking at this aspect of minitest was much shorter, but now that we understand what happened when our actual spec definition was evaluated, we can return to RSpec running and see how it’s actually exercised.
As we saw above, the describe method returns the created subclass of RSpec::Core::ExampleGroup, and when that is returned back in RSpec::Code::DSL#describe, the register method is called on it. This calls world.register with that class as an argument, where world is returned by RSpec.world and is an instance of RSpec::Core::World, which acts as a kind of global object to contain example groups, configuration and that sort of thing.
Calling register on the World instance stashes our Nested_1 class in an example_groups array within that world.
Our diversion is complete! You deserve a break. Go fetch a cup of your preferred delicious beverage, you have earned it!
Back in RSpec
OK, pop your brain-stack back until we’re in RSpec::Core::Commandline#run again. Our reporter did its preamble stuff, and we were iterating through @world.example_groups, whose origin we now understand.
For each example group, the run method is called on that class, with the reporter instance passed as an argument.
This gets a bit intricate, so I’m going to step through the method definition itself (for version 2.12.2) to help anchor things.
RSpec has a “fail fast” mode, where any single example failure will cause the execution of specs to finish as quickly as possible. Here, RSpec is checking whether anything has triggered this.
Next, the reporter is notified that an example group is about to start. The reporter can use this information to print out the name of the group, for example.
The run of the examples is wrapped in a block so it can catch any exceptions and handle them gracefully as you might expect.
The before :all hooks
The call to run_before_all_hooks is very interesting though, and worth exploring. A new instance of the example group is created. It is then passed into this method, where any “before all” blocks are evaluated against that instance, and then the values of any instance variables are stashed.
Consider our original example:
Given this, we’ll stash the value of @shared_thing (and the fact that it was called @shared_thing) for later use.
As you can see above, we can poke around with the innards of objects to our heart’s content. Who needs encapsulation, eh?
Why did RSpec have to create an instance of the example group class, only to throw it away after the before :all blocks have been evaluated? Because RSpec needs to evaluate the block against an instance of the example group so that it has access to the same scope (e.g. can call the same methods) as any of the specs themselves.
Running the example
Now we’re finally ready to run the examples:
To understand this, we need to look at the definition of run_examples:
This method iterates over each Example that was stored in the examples array earlier, filtering them according to any command-line parameters (though we are ignoring that here). The most relevant part for us lies in the middle:
Another new instance of the ExampleGroup subclass is created. Remember, RSpec created one instance of the class for the before :all blocks, but now it’s creating a fresh instance for this specific spec to be evaluated against.
Thinking back to how MiniTest works, there’s a striking parallel: where MiniTest would instantiate a new instance of the MiniTest::Unit::TestCase for each test method, RSpec is creating a new instance of the ExampleGroup subclass to evaluate each Example block against.
Instances of this class are used so that any methods defined as part of the spec definition are implicitly available as methods to be called in the “setup” and “test” bodies (see the module_eval section above). Not so different after all, eh?
Next, the instance variables that we stashed after evaluating the before :all blocks are injected (effectively using instance_variable_set as we saw above) into this new instance, which will allow the spec to interact with any objects those blocks created. It also means that these values are shared between every spec, and so interactions within one spec that changed the state of one of these instance variables will be present when the next spec runs.
Finally, the #run method on the Example subclass is called, passing the ExampleGroup instance and the reporter. Down one level we go, into Example#run…
The spec finally runs
Here’s the full definition of RSpec::Core::Example#run:
defrun(example_group_instance,reporter)@example_group_instance=example_group_instance@example_group_instance.example=selfstart(reporter)beginunlesspendingwith_around_each_hooksdobeginrun_before_each@example_group_instance.instance_eval(&@example_block)rescuePending::PendingDeclaredInExample=>e@pending_declared_in_example=e.messagerescueException=>eset_exception(e)ensurerun_after_eachendendendrescueException=>eset_exception(e)ensure@example_group_instance.instance_variables.eachdo|ivar|@example_group_instance.instance_variable_set(ivar,nil)end@example_group_instance=nilbeginassign_generated_descriptionrescueException=>eset_exception(e,"while assigning the example description")endendfinish(reporter)end
For our purposes, we again only need to consider a small part. Once all the reporter and “around” block housekeeping has taken place, the essential core of the example is run:
The call to run_before_each introspects the hook registry and evaluates every relevant before hook against the ExampleGroup instance. In effect, this will find any before blocks registered in this example group, and then any blocks registered in any parent groups, and evaluate them all in order, so that each nested before block runs.
Then, the spec block (stored in @example_block) is evaluated against the ExampleGroup instance. This is where your assertions, or matchers, are finally – finally! – evaluated.
If there was a problem, such as a matcher failing or an exception being raised, then the exception is stored against this Example for later reporting. Just as MiniTest assertions raise an exception when they fail, RSpec matchers raise an RSpec::Expectations::ExpectationNotMetError exception. It seems this is the universal way of halting execution when a test fails7. Another hidden similarity between RSpec and MiniTest!
As in MiniTest, whether or not the spec failed or an exception occured, an ensure section is used to guarantee that run_after_hooks is called, and any teardown is performed.
After the specs have run
Once all the specs in this example group have run, all the examples in any subclasses are run (recall that the inner describe stashed the nested ExampleGroup subclass in an array called children). We map each ExampleGroup subclass to the result of calling run on it, which starts this whole process again, for every nested example group. Whether or not this group passed or failed overall is then determined using simple boolean logic:
You can once again pop your brain-stack back until we’re in RSpec::Core::Commandline#run.
Having run all of the example groups, RSpec will do a little bit of tidy up, and finally return back up through the stack. Along the way printing the results of the run to the console is performed, before interpreter finally, properly quits.
the stashing of behaviour blocks, later evaluated using instance_eval against clean test-environment instances (see this section of the MiniTest article for what I mean by “test environment”);
using module_eval and subclassing to ensure method definition matches programmer expectation.
I would say these two aspects are the hallmark attributes of an RSpec-style test framework. The other notable aspect is the ability to nest example groups, and the subsequent necessity to be able to gather the implicit chain of setup blocks and evaluate them against the test environment instance, but this could be considered another example of using instance_eval.
Supporting method definition in example groups
One thing I’ve found particularly interesting is that RSpec ends up generating classes and subclasses behind the scenes. I believe this is almost entirely a consequence of wanting to support the kind of “natural” method definition within group bodies (see the module_eval section again).
If any test framework chose to not support this, there’s almost certainly no reason to create classes that map to example groups at all, and the setup and test blocks could be evaluated against a bare instance of Object.
Supporting nesting and dynamic composition
It’s clear that RSpec has more “features” (e.g. nesting, before :all and so on) than MiniTest (ignoring the many extensions available for MiniTest, the most sophisticated of which end up significantly modifying or replacing the MiniTest::Unit.run behaviour). I’m deliberately ignoring features like matchers, or a built-in mocking framework, because what I’m most interested in here are the features that affect the structure of the tests.
It’s certainly possible to implement features like nesting using subclasses and explicit calls to super, but this is the kind of plumbing work that Ruby programmers are not accustomed to accepting. By separating creation of tests from Ruby’s class implementation, the implicit relationships between groups of tests can take this burden instead, and behaviours like before :all, which have no natural analogue in class-based testing, are possible.
Now, you may believe that nesting is fundamentally undesirable, and it is not my present intention to disabuse you of that opinion. It’s useful (I think) to understand the constraints we accept by our choice of framework, and I’ve certainly found my explorations of MiniTest and RSpec have helped clarify my own opinions about which approach is ultimately more aligned with my own preferences. While I wouldn’t say that I’m ready to jump wholesale into the RSpec ecosystem, I think it’s fair to say that my advocacy of class-style testing frameworks is at an end.
RSpec and Kintama
I started this exploration because I wanted to understand the relationship between the software I have accidentally produced and what’s already available. I already had strong suspicions that any block-based testing implementation would converge on a few common implementation decisions, and while I have now identified a few interesting (to me) ways in which RSpec and Kintama diverge, the essential approach is the same.
In the final article in this triptych (coming soon, I hope), I’ll walk through Kintama and point those out.
There’s no built-in way to ‘nest’ test groups with MiniTest, or test-unit; the closest simulation would be to create subclasses, and explicitly ensure that super is called within every setup method.↩
There are other RSpec features like shared examples and global before/after hooks that are definitely interesting, but I need to keep the scope of this article down…↩
I’m not sure why some people prefer the syntax Module::method rather than Module.method; as I understand it they are exactly the same, but the former seems more confusing to me, since if you don’t notice the lower-case w in world then you’d assume it was refering to a constant.↩
It’s not really magic, and it’s not really “metaprogramming”, because it’s all just programming. It just so happens that it’s quite sophisticated programming.↩
The nested class is a subclass of the outer subclass of ExampleGroup (sorry, I realise that’s confusing), precisely such that any methods defined in the outer class are also available in nested subclasses via the regular mechanisms of inheritance.↩
Raising an exception might not be the only way to stop a test executing at the point it fails; it could be possible to use fibers/continuations to “pause” failing tests…↩