Why is nobody using Refinements?

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.

This is a transcript of a presentation I gave at RubyConf 2015; the actual slides are here; the video is from confreaks.

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.

Disclaimer

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.

Let’s look at a very simple example:

module Shouting refine String do def shout self.upcase + "!" end end end

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.

class Thing using Shouting "hello".shout # => "HELLO!" end

When we do this, all instances of that class, within the same scope as our using call, will have the refined methods available.

Another way of saying that is that the refinement has been “activated” within this scope.

However, any strings outside that scope are left unaffected:

"hello".shout # => NoMethodError: undefined method 'shout' for "Oh no":String

Changing existing methods

Refinements can also change methods that already exist

module TexasTime refine Time do def to_s if hour > 12 "Afternoon, y’all!" else super end end end end

When the refinement is active, it is used instead of the existing method (although the original is still available via the super keyword, which is very useful).

class RubyConf using TexasTime Time.parse("12:00").to_s # => "2015-11-16 12:00:00" Time.parse("14:15").to_s # => "Afternoon, y’all!" end

Anywhere the refinement isn’t active, the original method gets called, exactly as before.

Time.parse("14:15").to_s # => "2015-11-16 14:15:00"

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.

Using using

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:

class Thing using Shouting "hello".shout # => "HELLO!" end

But we can also move the call to using somewhere outside the class, and still use the refined method as before.

using Shouting class Thing "hello".shout # => "HELLO!" end

In the examples so far we’ve just been calling the refined method directly, but we can also use them within methods defined in the class.

class Thing using Shouting def greet "hello".shout end end Thing.new.greet # => "HELLO!"

Again, even if the call to using is outside of the class, our refined behaviour still works.

using Shouting class Thing def greet "hello".shout end end Thing.new.greet # => "HELLO!"

But this doesn’t work:

class Thing using Shouting def greet "hello" end end Thing.new.greet.shout! # => NoMethodError

We can’t call shout on the String returned by our method, even though that String object was created within a class where the refinement was activated.

And here’s another broken example:

class Thing using Shouting end class Thing "hello".shout # => NoMethodError end

We’ve activated the refinement inside our class, but when we reopen the class and try to use the refinement, we get NoMethodError again.

If we nest a class within another where the refinement is active, it seems to work:

class Thing using Shouting class OtherThing "hello".shout # => "HELLO!" end end

But it doesn’t work in subclasses:

class Thing using Shouting end class OtherThing < Thing "hello".shout # => NoMethodError end

Unless they are also nested classes:

class Thing using Shouting class OtherThing < Thing "hello".shout # => "HELLO!" end end

And even though nested classes work, if you try to define a nested class using the “double-colon” or “compact form”, our refinements have disappeared again:

class Thing using Shouting end class Thing::OtherThing "hello".shout # => NoMethodError end

Even blocks might seem to act strangely:

class Thing using Shouting def run yield end end Thing.new.run do "hello".shout end # => NoMethodError

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.

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

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:

  1. Once you call using, refinements are activated within the current, and any nested, lexical scopes
  2. 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.
  3. 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.
  4. 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.
  5. 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

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.

class String def camelize self.split("_").map(&:capitalize).join end end "ruby_conf_2015".camelize # => "RubyConf2015"

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?

Now we all know what caused the problem, but if someone else had written that code, I very much doubt it would be so obvious. And this is exactly the problem that Yehuda Katz identified with monkey patching in his blog post about refinements almost exactly five years ago.

Monkey-patching vs refinements

Monkey-patching has two fundamental issues:

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.

# Ruby 1.9 "hello".chars # => #<Enumerator: "hello".each_char> # Ruby 2.0+ "hello".chars # => ["h", "e", "l", "l", "o"]

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:

module OldChars refine String do def chars; each_char; end end end "hello".chars # => ["h", "e", "l", "l", “o"] using OldChars "hello".chars # => #<Enumerator: "hello".each_char>

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.

DSLs

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:

describe "Ruby" do it "should bring happiness" do developer = Developer.new(uses: "ruby") developer.should be_happy end end

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.

module RSpec refine Object do def should(expectation) expectation.satisfied_by?(self) end end end using RSpec 123.should eq 123 # => true false.should eq true # => false

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.

module UserAdmin refine User do def purge! user.associated_records.delete_all! user.delete! end end end # in app/controllers/admin_controller.rb class AdminController < ApplicationController using UserAdmin def purge_user User.find(params[:id]).purge! end end

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.

Breaking bad

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:

“Because they’re bad, aren’t they.”

As if it was a fact.

Now, my initial reaction to this kind of answer is somewhat emotionally charged, but my actual answer was more like:

“Are they? Why do you think that?”

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”.

Slow?

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.

And if that were true, then that would be totally legitimate… but it turns out it’s not:

““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.

UPDATE: Very shortly before this presentation was given, the first use of refinements actually was added internally within Rails.

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!

$ fgrep 'refine ' -R ruby-2.2.3/ext | wc -l # => 0

Many new language features, like keyword arguments, won’t see widespread adoption until Rails and the Ruby standard library starts to promote them.

Rails 5 has adopted keyword arguments and so I think we can expect to see them spread into other libraries as a result.

Without compelling examples of refinements from the libraries and frameworks we use every day, there’s nothing nudging us towards really understanding when they are appropriate or not.

4. Implementation quirks

There are a number of quirks or unexpected gotchas that you will encounter if you try to use refinements at scale.

For example, even when a refinement is activated, you cannot use methods like send or respond_to? to check for refined methods. You also can’t use them in convenient forms like Symbol#to_proc.

using Shouting "hello".shout # => “HELLO" "hello".respond_to?(:shout) # => false "hello".send(:shout) # => NoMethodError ["how", "are", "you"].map(&:shout) # => NoMethodError

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.

class Shouter def initialize(string) @string = string end def shout @string.upcase + "!" end end shouted_hello = Shouter.new("hello") shouted_hello.shout # => "HELLO!"

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”.

Conclusion-ish

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

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.

interblah.net - Getting Started with Spacemacs

Getting Started with Spacemacs

I wanted to capture what I like about my editor of choice at the moment. If you’re at all interested in modal editing, or are an experienced Vim user looking for a change, I think there’s a chance you might enjoy Spacemacs. I’ve written this to try and capture what I like about it, and some of the things I wish I’d known when I got started. Hopefully it’s useful to some of you!

Spacemacs

Table of Contents

My history with text editors

Probably every 18 months or so, I seem to get an itch about my current editor, and look around for something different. Often it’s because something is behaving slowly, or I can’t get a feature to work. My editors-of-choice over the last 15 years have been, roughly:

I’m sure all of the issues I experienced were largely my fault, rather than any problem with any of these editors, and I’m totally certain that any frustrations I experienced with any of them could be resolved with enough time and patience. However, when I’m sitting down to work on a project that already requires a lot of though, I don’t want to have to give up a lot of that energy to my editor; I need to be able to get it to do what I would like, with a minimum of energy, headscratching, or waiting for a spinny icon to finish.

Why Spacemacs?

Here’s what I really like about Spacemacs:

Much as I was initially resistant to it, putting all that work into using Vim for half a year did finally sink in, and I can pretty happily jump around within a line, or up and down blocks of code, without having to think too hard about it. I don’t know if I would entirely recant my rant, but the simple fact is that I’ve made the sacrifice and my brain has already been trained, so I might as well accept it. I don’t think I’ll ever be zealatous about the benefits of keeping your hands on the keyboard or anything like that, but I can say that once you do get a handle on movement and text manipulation commands, it doesn’t feel too bad.

… but only if you want it

Sometimes though, as a beginner, it’s just faster to use the mouse, or the arrow keys. And Spacemacs totally supports that. If I’m in a bad mood and don’t want to learn the modal way to do something, I can drag and select text, or jump the cursor around, and it just works. I can click on pretty much anything, from files and directories in the file tree, to links in a markdown document, and they do what you’d expect.

It’s this graceful support for non-modal mechanisms that makes it easy to stick with a modal editor through the rough times and the smooth.

Conventionality and discoverability via which-key

The biggest barrier to me sticking with Vim was feeling like “I know there’s a way to do this better” but having to enter the depths of the Google mines to figure out how. Vim is super-extensible and customisable, but the flip-side of that is that everyone has their own setup and key-bindings and so until you are an expert, you end up with a cobbled together configuration of stuff you’ve found on the internet. As I said above, I’m sure that eventually you would get a handle on this and be able to smooth any rough edges, but I think it’s a significant barrier for people who are already trying to internalise a whole new way of editing.

With Spacemacs, the main way you invoke commands is by hitting the spacebar. Press that nice big giant button and after about half a second, a menu appears with a whole bunch of options about what you might press next, with descriptions of what those are.

The menu, invoked via the spacebar

The commands are organised into sections, all based roughly on mnemonic groupings of what those functions do. So, if you want to do something with a file, chances are that it’s somewhere under the f section. If you want to change the project you’re working on, maybe try p. Want to search for something? Chances are it’ll be under s. Now these mnemonics aren’t perfect (“text” stuff is under x because t was used for “toggles”), but the descriptions are all right there, so the more you look at the menu, the more you internalise which section is which.

Once you get more comfortable, you often don’t need to look at the menu at all, because the combinations become internalised. I don’t need to look at the menu when I want to rename a file, because seeing the menu again and again has helped me learn that it’s SPC f R. Checking the git status of the project is SPC g s. What Spacemacs and which-key do really well is support the learning process, and get you to a point where you can invoke all these commands quickly.

Discovering new functions and key bindings

But it gets even better than that, because you don’t even need to dig around all the the menus until you find the command you were hoping for. If you have an inkling that there should be a more efficient way of, say, making some text uppercase without having to delete and re-type it, you can easily search all of the commands in the editor by hitting SPC SPC and then making a few guesses about what the command name might be. I hit SPC SPC and type “upper”, but none of those look right, so let’s try “upcase”, and bingo, a handful of functions that I might want to use.

Exploring the functions in Spacemacs

I can invoke one by hitting RET, but the menu also tells me what (if any) key conbination would run that function automatically, so if I end up doing this enough times, I’ll probably start trying that combo, and eventually not need to look it up ever again. Every instance of this helps teach me.

Showing the keybindings also shows me where in the menu I would’ve found that key, which over time also gives me clues about where to search for something the next time (“upcase region” is SPC x U, so perhaps there are other text case changing functions under SPC x…).

It also shows me when there are even faster ways of invoking the function using the “evil” bindings, in this case g U. Perhaps I’ll try that out, and if I try it enough times, it gets into my muscle memory, and I get faster.

Layers and conventionality

Learning how the menus are organised also leads to what I mean by “convenionality”. But before I explain this, I need to touch on how Spacemacs is organised. Rather than having you install lots of small Emacs packages (what you might consider plugins), Spacemacs gathers collections of related packages into what it calls “layers”. Every Spacemacs layer comes with a set of key bindings, which the community as a whole has developed together, gathering all of the features and functions of the packages within that layer together. So when you install the “ruby” layer, you get keybindings to run tests, to invoke bundler, to do simple refactorings, to use your preferred version manager, and so on, all ready for you to use and explorable via the menu and function search.

This is not to say that you can’t change things – you totally can! – but you don’t need to. There are similar distributions for other editors that enable similar “sensible” configurations, so this is not to say that Spacemacs is any better than, say, Janus for vim. but for any of these powerful editors, having something that provides this kind of support is really great.

Magit

From what I can tell, a lot of the love for Emacs comes from people talking about how org-mode is amazing and can do anything and it’s the greatest thing in the world, and that it’s worth switching to Emacs just to be able to harness the awesome power of org1. That might be true. I have not used it. I might discover how to use it tomorrow and achieve a similar text-based nirvana.

As a programmer though, there’s a different tool which has me totally sold on Emacs, and that’s Magit, a package for working with the Git version control system.

I used to be a command-line Git jockey. I’d use git add . and git commit -m "Blah blah", and even run interactive rebasing and patch-based adding from the command line. But if you care about creating a great git history, using git from the command line is working against you.

Magit makes it really easy for me to pick files to stage, or even just pick a few lines from those files, and commit just those changes, writing a great commit message while I do, right in my editor.

Using Magit in spacemacs

I really love Magit. With it can I do pretty much anything I knew how to do at the command line, and more, and it encourages me to write the best commit messages I can, and to keep my history tidy too. As far as I’m concerned, Spacemacs is a pretty decent editor with nice ways of teaching and reinforcing how to efficiently edit text, but Magit is a superpower.

So: Getting started

The title of this post is “getting started”, so I’m going to try to share what I did to get going with Spacemacs, in the hope it helps you get started quickly, and see some of the nice features that I’ve been enjoying.

As a caveat, let me say that I use a Mac, and I’m a Ruby developer using Git, so these notes are from that background, rather than a comprehensive set of instructions that will work for everyone.

Installation

Installing Spacemacs is pretty simple. Firstly, you’ll need Emacs. There are a few packages available via Homebrew:

I use emacs-mac because it has patched which enable pixel-level scrolling, and it can also be configured to run without a title bar, which I like because I tend to run Emacs maximised (but not “full screen” since that means something else these days in MacOS).

brew tap railwaycat/emacsmacport && brew install emacs-mac --with-no-title-bars --with-spacemacs-icon

One significant caveat of installing without title bars is that you can’t then drag the window around using your mouse. This can be a real pain, but if you always run Emacs maximised, it doesn’t matter. Unless you know you want this, I’d suggest installing without that option. You can use brew info emacs-mac to see all the installation options if you like.

Ripgrep

While we are at it, also install ripgrep, the super fast search tool that understands version control systems like git:

brew install ripgrep

Spacemacs will then use this tool to do fast searching. Next, install Spacemacs according to their instructions:

Getting Spacemacs

I would encourage you to just follow the instructions on the Spacemacs site, but the gist is this:

git clone https://github.com/syl20bnr/spacemacs ~/.emacs.d

Currently there’s a general recommendation to use the “develop” branch of the Spacemacs repository, since the “master” branch hasn’t seen much attention in a while. I find this a little odd, but that’s how they like to run the project I guess. So:

cd ~/.emacs.d && git checkout develop

Next, start up Emacs! You should see the Spacemacs home screen, and you’ll be prompted to answer a number of questions about how you’d like Spacemacs to be set up. I’d suggest you pick “Evil” as the editing mode, unless you know you don’t want to try to use the Vim-like modal system. Also, pick the “standard” distribution, rather than the minimalist one. Or at least, that’s what I did.

Now Spacemacs will set to downloading a whole bunch of packages and installing them, and this will take a few minutes. This is a perfect time read the Quickstart documentation while you drink your weak lemon drink.

Configuring your installation

Once all the packages have been installed, you’ll be presented with a simple help screen. Read it – it’s going to cover the basics fairly well. If you like, you can dive straight into editing files (take a look at the Cheat Sheet below which might help), but personally, I always like to see what I can tweak when I work with a new editor so once you’re done reading that, hit SPC f e d to open your personal configuration file. This is where all of the installed layers are declared and configured. I’m going to share the settings that I’ve used, but by all means tinker.

Layers

Here’s roughly what my layer configuration looks like:

javascript coffeescript yaml (osx :variables osx-use-option-as-meta nil) helm (auto-completion :variables auto-completion-tab-key-behavior 'complete) better-defaults emacs-lisp neotree git github markdown org (shell :variables shell-default-height 30 shell-default-position 'bottom) (version-control :variables version-control-diff-side 'left version-control-global-margin t) (ruby :variables ruby-version-manager 'chruby ruby-deep-indent-paren nil) (ruby-on-rails :variables feature-use-chruby t) html dash

The lines which aren’t wrapped in parentheses just use whatever defaults exist for the layer. Those that are (e.g. osx or shell) have been configured by me a little. Looking at the layer documentation will explain those settings, but the interesting parts there are that I’ve “unconfigured” the right alt key as the Emacs “meta” key, because I need that key to type # characters.

I’ve also set the Ruby version manager to be “chruby”, but you might need something else. If you want to stick with the defaults, just delete :variables and everything after it, and remove the parentheses.

Other settings

dotspacemacs-maximised-at-startup t

I tend to run Emacs full screen, so I want it to start that way.

dotspacemacs-line-numbers t

Always show line numbers in every file

dotspacemacs-whitespace-cleanup 'changed

For any lines that I have edited, make sure there are no trailing whitespace characters, but otherwise leave other lines untouched. I find this is the best compromise between keeping whitespace in check, but not having commits full of unrelated whitespace changes just because I touched a file that had some.

That’s it for the Spacemacs options, but you can configure other stuff inside the dotspacemacs/user-config function. Here’s a few things that I have:

;; Use two spaces as the tab width (setq-default tab-width 2) ;; Make the modeline look good on Mac OS X (setq powerline-default-separator 'utf-8) ;; Make the right alt key available as meta in case that's useful (setq ns-right-option-modifier 'meta) (setq mac-right-option-modifier 'meta) ;; When saving using cmd-s, exit back into normal mode so that other vim-ish commands just work (global-set-key (kbd "H-s") (lambda nil (interactive) (evil-force-normal-state) (call-interactively (key-binding "^X^S")))) ;; Note that the "^X^S" string above actually needs to be the proper `ctrl-x ctrl-s` characters, which I can't embed in my blog. Best copy them directly from my dotfiles at https://raw.githubusercontent.com/lazyatom/dotfiles/spacemacs-develop/dotfiles/spacemacs ;; Make cmd-t open the project file finder, like other editors (evil-define-key 'normal 'global (kbd "H-t") 'helm-projectile-find-file) ;; Include underscore in word movement commands for Evil mode (dolist (mode-hook '(ruby-mode-hook enh-ruby-mode-hook python-mode-hook js2-mode-hook haml-mode-hook web-mode-hook)) (add-hook mode-hook #'(lambda () (modify-syntax-entry ?_ "w")))) ;; don't align if statements to the if (setq ruby-align-to-stmt-keywords '(if begin case)) ;; set fill-column with for git commits to recommended width (add-hook 'git-commit-mode-hook (lambda () (setq fill-column 70))) ;; Follow symlinks to files without complaining (setq vc-follow-symlinks t)

OK, enough configuration. Let’s actually start editing some stuff!

You’re a vimmer now, ‘arry

First thing’s first, you’re going to need to get used to modal editing. Covering that is way outside of the scope of this article, but here’s some basics.

You’ll start in “normal” mode, which is for moving around, and for manipulating text without actually typing new stuff (so, like, moving lines, copying and pasting and deleting and so on).

If you want to enter some text, you’ll need to enter “insert” mode. There are a few ways to do this, but one is just hitting “i”. Then you can type as much as you like, and hit ESC when you are done to get back to “normal” mode.

To move the cursor around in normal mode, the vimmish way is by using the hjkl keys, which correspond to left, down, up and right. Left and right are pretty obvious, but I used to often get confused which of j and k was up or down. The best way I’ve heard of to remember this is that j looks a little bit like a down arrow, if you can stretch your imagination to that. Bear that in mind and it’ll soon become second nature.

There is a “lot” more to editing using Vim-like commands, and you’ll spend most of your time doing that, so if you’re not very experienced, I would suggest trying out one of the various vim tutorials. There’s even one built in to Spacemacs (SPC h T)!

Editing files and projects

Spacemacs uses a package called projectile to manage projects, but it can be a little unclear how to actually get started with this. Here’s a simple way to get into it.

You can browse the list of projects that Projectile knows about with SPC p p. However, when you get started, this list will almost certainly be empty. Projectile considers a project as any directory that’s under version control. Projects don’t really exist until Projectile discovers them, and the best way of doing that is by opening a file within that directory.

Hit SPC f f to start browsing for a file to open. You can start typing and hit TAB to autocomplete directory and file names, or use ctrl-h to jump up to parent directories to move around quickly. Once you’ve found the file, hit RET to open the file.

Depending on the type of the file, you might be prompted to add a new layer to get all the features for that type of file; it doesn’t really matter what you choose at this point.

Once the file is open, if you hit SPC p p again, assuming you’ve opened a file that was somewhere under version control, you should now see the project in the list, and you can get back to this project easily now.

When you do select a project using SPC p p, you’ll next be prompted to pick a file from that project. For a long time, this confused me, particularly as I moved from one project to the other, and back, in a single editing session. However, this is normal – Spacemacs is just asking which buffer or file from that project that you want to start on.

Cheat Sheet

These are all the commands that I wish I’d known when I got started. It’s also very worth getting a grounding in Vim, including the keys I listed nearer the top of this article; here I’m just including the Spacemacs-specific commands that cover the basics.

Spacemacs includes many other commands, and likely some of them will be better in certain situations than the ones I’ve listed. These are just the basic ones that kept me moving.

These are the basic commands that I use all the time:

There’s a bunch more that I do know and use, and I am certain that I could be using different commands to do a lot of stuff more efficiently, but I think it was really internalising the commands above that got me through the transition from feeling massively slowed down to reasonably efficient at editing and moving text around.

All of these are Vi/Vim commands, but Spacemacs implements them using a package called “evil”, which according to all the reckons on the internet that I’ve read, is hands down the most complete Vim emulation layer available. What this means is that any advice or tricks my Vim-using friends might have, I can also use too, and that’s pretty neat.

General

f d - in rapid succession, will quit out of most menus/commands/minibuffers back into normal mode

ctrl-g - tell Emacs to quit something that’s taking too long

SPC q q - quit Emacs

SPC q R - restart Emacs

q - if you’re in a non-file window, chances are if you hit q, it’ll close that window. Give it a try.

Files, projects and buffers

SPC f f - browse for files and open them

SPC f r - browse for a recently-opened file

SPC f R - rename the current file

SPC f D - delete the current file

SPC p p - show list of projects to choose (you will be prompted to open a file in that project afterwards)

SPC p f - file a file within the current project

SPC p b - show a list of all open buffers for this project (buffers are sort of like “open files”)

SPC b b - show a list of all open buffers

SPC b d - delete a buffer, which basically means “close this file, I don’t want to see it anymore”

SPC TAB - switch back to the previous buffer

SPC f t - toggle open a file tree (use shift-k to go up to parent directories in it)

Windows

SPC w / - split a window vertically

SPC w s - split a window horizontally

SPC 1/2/3...9 - jump to window 1 to 9 (you should see the window numbers in the little mode line)

SPC 0 - jump to the file tree (if it’s open; might actually open it if it isn’t)

SPC w d - close the current window

SPC w m - make the current window the only window

Searching

SPC / - search every file in the project

SPC s s - search (“swoop”) in the current buffer

SPC * - search the current project for the symbol/word under the cursor

* - enter a powerful search “mode” for the symbol/word under the cursor

Editing

SPC i k/j - insert a blank line above or below the cursor without entering insert mode

SPC ; ; - comment out a line of code (although now I know about g c c and all the other Vim-ish g c commands)

Development specific stuff

, t b - run all the tests/specs in this file

, t t - run the test/spec at the cursor

SPC c d - close the test window (“c” is for compilation)

SPC c k - quit the current test run

SPC p a - switch to the alternate file for this one (e.g. from controller to controller test, and back)

Git

SPC g s - open git status. Then hit ? to see the keys that Magit understands. Note that case is important. Hit q to close the Magit buffer.

Spacemacs itself

SPC f e d - open your configuration file

SPC f e R - reload your configuration after editing that file

SPC f e D - compare your configuration to the default one. This is super useful when you’ve updated Spacemacs, since new configuration options become available. When you do this, you enter an “ediff” mode; hit ? to see what keys do, but the basics are: press n and p to move between changes, then a or b to pick which option to use, and q to quit

SPC SPC - search for a function by name, and run it. This is a great way of getting a sense of what Emacs/Spacemacs can do

Documentation and help

Emacs and Spacemacs come with a huge amount of documentation built in, which is extremely useful when you’re trying to figure out how to do something.

SPC h l - search for a layer and then open its documentation, which will show all its configuration options and key bindings

SPC h d f - search for a function and show its documentation

SPC h d v - search for a variable and show its documentation, and current and default values

SPC h d p - search for a package and show information about it

SPC h d k - then enter any combination of keys, and Spacemacs will tell you what function they are bound to and what it will do.

Help, I’m Stuck!

As with any complicated bit of software, it’s possible to get stuck in a menu or lost in some options, and want to back out. Sometimes key bindings you expect to work, will not, because you’ve ended up in one of the “system buffers” that Emacs uses to store command output or something else. Don’t worry – it happens to everyone.

The best way I’ve found to re-orient myself is to hit SPC f r or SPC b r to open the recent file or buffer list, and then get back to a file that I was working on. From there, I can normally get back to moving around my project like I expected. Worst case, just quit (SPC q q) and open Emacs again.

Other Notes

Spacemacs works just as well in the console version of Emacs as the GUI one, so you can happily sync your configuration to remote machines and use everything you’ve learned when editing files on remote servers. And take a look at “Tramp” if you’re interested in another Emacs superpower involving remote files!

Even though I’ve been using Spacemacs for the best part of a year now, I only recently actually read through the documentation (SPC h SPC RET inside Spacemacs, or here). There’s a lot of good stuff in there, particularly about the evil keybindings, and things like managing layouts and workspaces. Once you’ve gotten to grips with some of the basics, I highly recommend just browsing it, and then coming back to it every now and again, to see what jumps out in an “ah! I can use that” kind of way.

I hope this has been useful, either if you’re curious about Spacemacs, or are playing around with it. Thanks for your time!

  1. Perhaps, you might say… joining the borg? Groan

  2. With Spacemacs I actually have it set up so I can hit cmd-s to both save the file and enter normal mode, though I recently also learned about fd and am trying to practice that.