Skip to content

#include? vs #match

TL;DR: String#include? seems to be faster than String#match, which seems to be a result of not having to compile a regular expression. Adding an end-of-line anchor to the regexp implementation gives a slight performance improvement, but not enough to make it the optimal approach.

Today we were working on an implementation of #method_missing where we check to see if the called method was a predicate method, and if so, look for the existence of a corresponding attribute in a config object (an OpenStruct, in this case.) Probably more context that necessary but at any rate we wanted to know whether it was faster to use String#include? or String#match, so we came up with the following benchmark:

Based on these numbers, the choice to use #include? is quite obvious.

We decided to take a spin at refactoring the benchmark code (‘just for fun’, ‘because we can’, ‘why not?’).sample to see if we could alleviate some of the duplication.

Our first refactoring attempt was less than ideal; the way we used blocks seems to have skewed the result, making it look like the regexp implementation is equal or faster, but that result is inconsistent with the more explicit implementation, which I have more faith in.

Then we came up with this, which increased overall execution time, but kept the difference in implementation speeds relatively consistent, and so would probably serve well enough as a benchmarking harness for most simple cases where you want to determine how much performance one approach yields over another, without needing to know the exact execution time.

Results:

Finally, here is the implementation of #method_missing we landed on:

Hope this is useful and educational, and as always, that you are having fun and challenging yourself!

Just Show Up.

“Eighty percent of success is showing up.” – Woody Allen

“Ninety eight percent of all statistics are made up.”  - Author Unknown

“There’s only one way to make a beginning, and that is to begin” – Jack London

Context: You are bored, stagnating, or feeling undernourished in your personal or professional life. Even for individuals fortunate enough to have a career in software development, it can happen.

Problem: You want to change some aspect of your life, to learn and to grow, but you are uncertain and or stressed over how, or what the next step might be, or what the process is, or where to start or whether you might be over-thinking it…

Solution: Consider something you’ve been interested in recently, or may have enjoyed in the past (reading, cycling, web development, etc.) and find a group of people nearby who are doing that thing. If you feel like you haven’t been reading enough, find a book club. Want to learn guitar? Find an open mic night. Curious about Javascript? The pattern is as simple as it seems.

In healthy, supportive (software) communities, even introverts should find a comfortable environment where they can listen and learn as people meet, share knowledge, and build their respective support networks. See what people are talking about, get ideas, learn new practices, maybe even Find Mentors to help you grow. (They will benefit too, teaching is a great way to learn.)

If you decide that your new skill, group, or practices are not helping you attain the value you sought, or you feel more compelled to try some other more worthwhile activity, do it. Move on. Hopefully the last time you showed up at something it removed some of the fear or discomfort (especially for socially awkward penguins,) and from now on it will be easier to repeat the process of finding a community and deciding whether to participate in it. It’s possible, perhaps even likely that you will enter the next stage of your journey having made some new friends and learned new things in the last one.

The ultimate goal of Just Showing Up is to leave a state of inaction for one of action. It is often challenging; the distance from nothing to something (0 to 1, for example) is potentially infinite. Overcoming a theoretically infinite amount of inertia is daunting, and can be overwhelming.

However, it has been my experience that once you learn to let go of your fear or whatever holds you back (another lifelong process,) the pace of learning and growth accelerate rapidly and interesting opportunities & experiences take shape which would have been previously unfathomable.

Action: Decide on a new hobby, activity, or technology. Find a local community of people interested in the same thing, and attend their next meeting. Find Mentors, Rub Elbowsmeet Kindred Spirits. You can do it. Now, go on.

Mentors

Cliché or no, this thought begins with a definition;

men·tor/ˈmenˌtôr/
Noun: An adviser.
Verb: To advise or train (someone).

Sounds like a teacher.

teach·er/ˈtēCHər/
Noun: A person who teaches, esp. in a school.

Strange, what’s the difference between teaching and training?

teach/tēCH/
Verb: Show or explain to (someone) how to do something: “she taught him to read”.

train/trān/
Verb: Teach (a person or animal) a particular skill or type of behavior through practice and instruction over a period of time.

Immediately the difference becomes clear. Training involves teaching, but it also involves a more significant investment of time, effort, and energy.

It makes sense. I can remember very little of what most of the teachers I’ve interacted with in various classroom settings have said. There are a few exceptions. Specifically, those instructors who gave an extra few minutes for a conversation to explore a topic in more detail, or answer a longer form question unsuitable for classroom dialog. They stand out as memorable, as having a voice and a face, a character in history as I remember it.

In career contexts, I’ve had mentors. People who have taken time to understand how my thought process functions, figure out what was important for me to learn, and teach, lead, and instill values that would allow me to help and teach and grow in ways not previously imagined. As much as technical articles suit me, since writing about “I, me, my” feels pedantic and uninteresting, it felt appropriate in the spirit of yesterday’s celebration of love and kindness to spend some time this morning reflecting and being thankful for people who have held the title of mentor in my life.

The world as I’ve seen it so far (damn, there he goes again) is full of people eager to learn, to challenge themselves, to do something inspiring. Those are people who can benefit from mentors. WE NEED MORE MENTORS! Paradoxically, the thought of leaving an impression on the life of a fellow human can be intimidating. So, many people never consider themselves worthy, able, nor compelled or somehow otherwise prohibited from or uninterested in shouldering the responsibility of being a mentor.

Perhaps it’s not for everyone. Perhaps it takes, as they say, a “particular brand of crazy.” Perhaps it’s a matter of perspective and confidence; the feeling that one must be first an expert or master, then a teacher. This is simply not the case.

One particularly relevant conversation with a particular mentor comes to mind. Wherein, the following words were spoken which resound even still: “Just because you know something, that doesn’t mean everyone knows it. And just because you don’t know everything doesn’t mean you don’t know anything. You don’t always have to be the expert. You just have to be an hour ahead of the next person who wants to learn what you’re learning.” It’s possible that’s somewhat paraphrased (it definitely is) but I believe the spirit remains intact. This person was also fond of the saying “Never take heed of your fears.” Again, paraphrased.

How great are commas, eh? Like, they’re really, well, simply put, the best, the most useful, just the best thing ever, really.

At any rate there’s a lesson or a point or something worth all the lead-in, right? Let’s hope. Otherwise why on earth are you still reading this (or whatever planet you’re reading this from…)

There is, and it is this. You can help someone. You can teach somebody, and even better if you give it time you can train them, and mentor them. If you’re doing something, making something, investing time and energy in something you can combine your time and effort with that of another human and do awesome things that will inspire others to learn and improve and in turn teach someone else. It’s just the way things work. There are all sorts of questions about what’s worthwhile to do, they’re less relevant than you might think. The lessons learned by a student won’t be limited to those which you intend. Let’s assume that what you can do or teach is worthwhile to someone. Don’t worry about being an expert. Invest a bit of time and effort in getting to know someone, see what they know that you don’t, and vice versa, and as long as you aren’t carbon copies of one another who have exactly the same knowledge and experience, agree to a common objective and pursue it together. The rest will take care of itself. We are all mentors whether we realize it or not. Might as well be intentional about it.

Lastly (finally, I thought this windbag would never shut up) a word of thanks (didn’t you just write 700 of them) to my mentors. To all of the mentors. You’ll never know how much your time is worth to the people you give it to. Just know that it’s worth much more than any number of words could ever adequately convey.

Sincerely,

A student trying to make sense out of this ball of confusion (seriously? is that supposed to be a segue? just, wow…)

Ruby 1.9.3 on OSX 10.7 (Lion)

It’s been causing nothing but headaches, trying to install 1.9.3 or any newer versions of 1.9.2, ever since upgrading to Lion.

Finally, after a few failed attempts and several stackoverflow threads later, this article surfaces and wouldn’t you know; it seemed to get the job done.

Good job, author of that blog. Your work has been most helpful!

Metrics.

Metrics are challenging. (Grammar fanatics; weep, and begone.)

Experts beware: this is beginner stuff.

When properly applied, they can shed light on relationships or help to convert data (what happens in a system) into information (insights that inform decisions or actions.)

However, they can also become a performance target. Take, for example, the case of team velocity; that is the capacity of the team to reach objectives over time. If the expectation of a business or company is that velocity will continually increase, or, if this is put forth as a goal, the team in charge of that expectation is well positioned to fail. Some teams, however, in an effort to show or prove value will try to consistently improve their velocity number; by inflating estimates on a story level, or doing the unthinkable. The unspeakable.

Taking shortcuts in the code. Yep. That’s right. Accumulating technical debt like a wasted socialite on a shopping spree accumulates paparazzi.

Folks, just say no to shortcuts.

The point is, any metric can easily be gamed or somehow manipulated to prove a point for either side of just about any argument I can think of. They’re pieces of a picture, not the whole thing.

With the goal of using them more constructively, I propose the three following questions as a test for determining whether a metric is worth an investment of time:

  1. What facet of the business or system will the metric explain?
  2. How will an understanding of the measured aspect benefit your stated goal?
  3. What is the set of data being studied?

The reasoning behind numbers one and two should be self explanatory. The third item is important because a metric which cannot be constrained to a finite set of dimensions, as far as I am aware, is very difficult to understand, let alone to apply constructively. That said, my expertise is minimal in this regard, and certainly someone more qualified could better elucidate the idea of how to measure an infinite number of dimensions.

Let’s agree to be less frivolous with our metrics, shall we?

Great. Now, some frivolous Metric:

Also, Solomon made this for you:

a.chunk{|n| n }.map{|n| n.first }

it will remove subsequences from an array. Like this:

ruby-1.9.2-p290 :056 > a = ["a", "a", "b", "a", "a", "c"]
=> ["a", "a", "b", "a", "a", "c"]
ruby-1.9.2-p290 :057 > a.chunk{|n| n }.map{|n| n.first }
=> ["a", "b", "a", "c"]

let(:me) {get(that).for(you)}

Before blocks in rspec are good for factoring out certain types of common setup (stubs, in particular.) However, they are not the ideal solution for every test setup need. For example, they’re commonly abused to create one or more instance variables that are used as test data, or assertions are made against them, etc.

Typical shared setup block in rspec

Rspec provides another (and sadly perhaps lesser known) facility for accomplishing the same end in a more efficient fashion. The lazily evaluated let(:symbol) {block} provides a way to extract objects (especially the current system under test), especially those which are used in multiple examples within one or more describe blocks, but perhaps not every example within a given describe or context.

The reason this is more efficient than an instance variable in a before block is that before blocks are executed on each test run, so those data values instantiated regardless, times n examples. That’s assignment cost, memory cost, and parsing cost.

Lazily evaluated blocks only incur cost when they are used. That is to say the parse cost is incurred regardless, but assignment and memory cost are only incurred in the examples which use the values returned by the instance methods that let() creates. It is easy to factor example 1 to use let blocks instead of a before block (and looks cleaner, in my opinion.)

Shared setup, using let()

Another benefit is that lazy evaluation provides a facility to pass different data in from each spec, since the block is evaluated when the method is called, which can be after some individual setup occurs.

Let blocks allow for flexible common setup definition

As you can see, let() can be a very powerful and useful construct, if properly employed. I first discovered let() in a slide deck entitled Pure RSpec. Check it out for this and other fun RSpec goodness, and as always, happy hacking!

Test Generators, and Other Thoughts on Style.

For a time, I paired with someone who convinced me that whitespace between example blocks was superflous and so wound up with a bunch of specs like:

it "should define ACTIVITY_LEVELS" do
  Game::ACTIVITY_LEVELS.should == %w{ Low Medium High }
end
it "should define FORMATIONS" do
  Game::FORMATIONS.should == %w{ Circle Lines Scattered None }
end
it "should define LOCATIONS" do
  Game::LOCATIONS.should == %w{ Indoor Outdoor }
end

which now bothers me; whitespace separation between example blocks feels more idiomatic. In fact, someone asked me after seeing those specs “are you in a fight with whitespace?” I am not.

Also, I don’t like “should” in descriptions. Too wishy-washy sounding, and again, superflous. Speaking of superflous, spaces between curly brackets and their contents… they are it.

Really, does reading the first example require that much more effort than the second?

{ :some_key => "some value" }

vs

{:some_key => "some value"}

Call me crazy but it saves two keystrokes and just looks better to me without the spaces. Style call. YMMV.

Another thought on idiomatic tests for equality in Rspec examples suggests using the appropriate “eq” and “eql” matchers instead of the equality operator (“==”) so that’s made its way into muscle memory lately as well.

So, given the above, were I to refactor or rewrite those specs today, they would look like this:

it "defines ACTIVITY_LEVELS" do
  Game::ACTIVITY_LEVELS.should eql(%w{Low Medium High})
end

it "defines FORMATIONS" do
  Game::FORMATIONS.should eql(%w{Circle Lines Scattered None})
end

it "defines LOCATIONS" do
  Game::LOCATIONS.should eql(%w{Indoor Outdoor})
end

Looks a bit cleaner to me, probably good enough.

However, since the description isn’t really giving us any brilliant insights about the behaviour the specs are verifying, those examples could probably be factored into specify blocks:

specify {Game::ACTIVITY_LEVELS.should eql(%w{Low Medium High})}
specify {Game::FORMATIONS.should eql(%w{Circle Lines Scattered None})}
specify {Game::LOCATIONS.should eql(%w{Indoor Outdoor})}

It’s terse, but still pretty readable and obvious to most people who are familiar with Rspec (and we know our tools, right, so that’s not an issue.)

That is probably a reasonable stopping point, providing it makes sense to leave those as explicit examples. However, were we to add a few more examples, it might be sensible to consider rolling them into a test generator. It can be a useful pattern, especially if you are testing the initialization of many attributes for e.g. an API call, or some similar thing.

While this may not be the ideal example, here’s what that might look like in the context of the above specs:

constants = {:activity_levels => %w{Low Medium High},
             :durations => ["5 to 15", "15 plus", "below 5"],
             :formations => %w{Indoor Outdoor}}

constants.each {|key,val| specify {Game.const_get("#{key.upcase}").should eql(val)}}

In this case it actually adds a line, and again these particular specs are simple enough that leaving them as three individual calls to specify is not a huge deal, but imagine you were testing something like the mapping and initialization of an object from the result of an api call;

result_hash = {:some_key => "some value",
               :some_other_key => "some other value",
               :another_key => "another value",
               :yet_another_key => "yet another value",
               :more_key_fun => "more value fun",
               :moar_object => "moar stuff"}

it "maps some_key to some value" do
  SomeApiWrapper.new_from_api_result(result_hash).some_key.should eql("some value")
end

it "maps some_other_key to some other value" do
  SomeApiWrapper.new_from_api_result(result_hash).some_other_key.should eql("some value")
end

… and so on. Again, taking into account that this is a made-up example, consider that explicitly specifying that all 6 of the expected keys are properly initialized would require 18 lines of code, plus whitespace. Yes, you could build the object and assert that the result of the method you’re testing returns an equivalent object, but sometimes that isn’t as easy as it sounds, or you only need to test certain attributes… Point being, if you’re writing specs like those, you might consider using a generator, like this:

result_hash = {:some_key => "some value",
               :some_other_key => "some other value",
               :another_key => "another value",
               :yet_another_key => "yet another value",
               :more_key_fun => "more value fun",
               :moar_object => "moar stuff"}

result_hash.each do |key,value|
  it "maps #{key} to #{value}" do
    SomeApiWrapper.new_from_api_result(result_hash).send("#{some_key}").should eql("some value")
  end
end

And save ourselves some typing, some reading, and some duplicationey stuff. Since we’re kind of on a roll here, let’s save ourselves some method calls too by moving the actual call out of the example:

result_hash = {:some_key => "some value",
               :some_other_key => "some other value",
               :another_key => "another value",
               :yet_another_key => "yet another value",
               :more_key_fun => "more value fun",
               :moar_object => "moar stuff"}

actual_result = SomeApiWrapper.new_from_api_result(result_hash)

result_hash.each do |key,value|
  it "maps #{key} to #{value}" do
    actual_result.send("#{some_key}").should eql("some value")
  end
end

Congratulations. Our spec is now precious few microseconds faster. Imagine if that method were more computationally intensive, however. The longer that method call takes, the more time that one little change saves.

The last thing bothering me about that spec is the questionable value of explicitly specifying that it maps some key to some value. We could probably use the “specify” method and clean things up just that much more… this is DEFINITELY a style issue however, because the following is quite terse and may be difficult for some people to read, or understand what the spec code is really doing:

result_hash = {:some_key => "some value",
               :some_other_key => "some other value",
               :another_key => "another value",
               :yet_another_key => "yet another value",
               :more_key_fun => "more value fun",
               :moar_object => "moar stuff"}

actual_result = SomeApiWrapper.new_from_api_result(result_hash)

result_hash.each do |key,value|
  specify {actual_result.send("#{some_key}").should eql("some value")}
end

That looks pretty clean, from here. All of that said, writing for readability is never bad, some duplication in test code is usually acceptable, and use your discretion about how “clever” or dense it makes sense for your specs to be. As long as you’re writing them, you’re that much better off than someone who isn’t.

Again, allow me to reiterate that these are largely matters of preference and style, and are by no means to be taken as anything but. That said, feel free to share your rants, raves, questions or flame war inciting opinions below.

Follow

Get every new post delivered to your Inbox.