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

HTML5 <video> and <audio> – supported formats and browser compatibility

16 Apr

With HTML5 video and audio tags are here and ready for use to easily enhance your websites with audio and video. The tags are available in all major browsers now, except for Opera Mini (audio video). You even got popcorn.js to interact with the media, make you web pages react to the progress of a video and build cool new media enriched websites.

The major problem with HTML5 media though so far has been browser support for the different media formats/codecs. Support has gotten a lot better – not great, but better. At the time of this writing it seems like you only have to offer 2 different formats for audio and video to support a wide range of browsers.

Disclaimer: I didn’t try this all out manually. I trust the data for Browser compatibility I found on the Internet: Mozilla Developer Network media format support, Wikipedia(HTML5 audio, HTML5 video)

Audio

Support mp3 and Ogg Vorbis – you can use other formats in place of ogg as well (and AAC in place of mp3).

Video

Support H.264 (.mp4) + Theora (.ogv) or VP8 (WebM) should do the trick.

On a last not, if you want to convert video files you can use ffmpeg, e.g. for instance theora/.ogv to H.264/WebM:

ffmpeg -i demo.ogv -f mp4 demo.mp4

Hope that this helped :-)

 

 

Follow

Get every new post delivered to your Inbox.

Join 854 other followers