Scott Hanselman

The Weekly Source Code 29 - Ruby and Shoes and the First Ruby Virus

June 24, '08 Comments [11] Posted in Ruby | Source Code
Sponsored By

I was in Norway last week at the Norwegian Developers Conference, so I'm afraid the Weekly Source Code is the bi-weekly source code this month. It's amazing how international travel can really slow a body down. This trip just obliterated me. I'm slowly coming back to the world of the living, though.

I've been getting more and more interested in how folks extend their applications using plugins and things. In my new ongoing quest to read source code to be a better developer, Dear Reader, I present to you twenty-ninth in a infinite number of posts of "The Weekly Source Code."

This week's source is a clever "ClickOnce"-style hack for Ruby. It's cool because it brings together a number of technologies into a very clean end-user experience. The intent is to make the running of a Ruby GUI Application effortless, and it works and it's brilliant on several levels. Thanks to Sam Saffron for pointing me to it!

To start out, it's a hack says the author why the lucky stiff (or "_why" for short), so it's very early and there's bugs. You can check out the code on GitHub: http://github.com/why/shoes/tree/master or clone the whole tree using git via "git clone git://github.com/why/shoes.git"

Shoes

Ruby is a very aesthetically (to me) pleasing and flexible language. Shoes is a GUI Toolkit for making Windowing Applications using Ruby. I mentioned it back in TWSC12. Shoes is legendary for a number of reasons, but above all, it has the greatest API documentation in the history of all software documentation. The main book on Shoes Development called "nobody knows shoes" is fantastic. Even Chris Sells likes it!

Here's part of an example phone book application written in Ruby and Shoes):

Shoes.app :width => 320, :height => 350 do

stack :margin => 40 do
stack :margin => 10 do
para "Name"
@name = list_box :items => ["Yes, please!", "NO. No thankyou."]
end
stack :margin => 10 do
para "Address"
@address = edit_line
end
stack :margin => 10 do
para "Phone"
@phone = edit_line
end
stack :margin => 10 do
button "Save" do
Shoes.p [@name.text, @address.text, @phone.text]
end
end
end
end

It feels a lot like Tcl/Tk or Rebol. It'll be interesting to see if someone writes a mainstream application that is non-trivial. Shoes works on XP, Vista, MacOSX and Linux. Do note that you'll need to right click and run it as Administrator on Vista as _why hasn't put in an elevation prompt yet. You'll need Admin to do the initial bootstrap install.

However, in order to get Shoes to work, before today, you had to install Ruby, then get the Shoes libraries installed, usually via a gem. Then you'd need to run your app via ruby.exe yourapp.rb or a shortcut you'd setup.  The second comment on _why's blog post announcement says it all:

"Wow, this is super. We can actually give our apps to other people now :)"

Here's an explanation from _why on how the Shoes "bootstrapper" (my word) works:

If you poke around with a hex editor inside Windows’ PE binary format, you’ll find an .rsrc section at the end of the file which contains the icons and dialog boxes. I insert the Ruby script into this mess.
binj = Binject::EXE.new("blank.exe")
binj.inject("SHOES_FILENAME", "simple-accordion.rb")
File.open("simple-accordion.rb") do |f|
  binj.inject("SHOES_PAYLOAD", f)
end
binj.save("accordion.exe")

Fantastic hack. He shoves the Ruby script into a Windows Resource! He does the same thing for DMG (Disk Images) on Mac OSX, so you can create Ruby scripts that are portable to these two platforms.

When it runs, it checks for the existence of Shoes on your system. It's in C:\Program Files (x86)\Common Files\Shoes\. If Shoes (which includes a private copy of Ruby) is not there, it's downloaded and installed, and then your app runs. Bam, one-click cross-platform GUIs.

I'm not clear what the security ramifications are. Well, I am. I suspect there is no security that isn't already provided by the host operating system. Once you're running, the Ruby script has full control to do whatever it likes, so I suppose someone could write a malicious program just as they could write a malicious .NET app if they liked.

You can package your apps just by running shoes --package and you'll get this dialog. You can chose to include Shoes and Ruby inside the resulting EXE if you like.

 Shoes

Shoes also supports custom "Widgets." For example, here's a custom Speedometer control.

Shoes (2)

The code for the entire app, including the custom control is this:

class Speedometer < Widget
attr_accessor :range, :tick, :position
def initialize opts = {}
@range = opts[:range] || 200
@tick = opts[:tick] || 10
@position = opts[:position] || 0
@cx, @cy = self.left + 110, self.top + 100

nostroke
rect :top => self.top, :left => self.left,
:width => 220, :height => 200
nofill
stroke white
oval :left => @cx - 50, :top => @cy - 50, :radius => 100
(ticks + 1).times do |i|
radial_line 225 + ((270.0 / ticks) * i), 70..80
radial_line 225 + ((270.0 / ticks) * i), 45..49
end
strokewidth 2
oval :left => @cx - 70, :top => @cy - 70, :radius => 140
stroke lightgreen
oval :left => @cx - 5, :top => @cy - 5, :radius => 10
@needle = radial_line 225 + ((270.0 / @range) * @position), 0..90
end
def ticks; @range / @tick end
def radial_line deg, r
pos = ((deg / 360.0) * (2.0 * Math::PI)) - (Math::PI / 2.0)
line (Math.cos(pos) * r.begin) + @cx, (Math.sin(pos) * r.begin) + @cy,
(Math.cos(pos) * r.end) + @cx, (Math.sin(pos) * r.end) + @cy
end
def position= pos
@position = pos
@needle.remove
append do
@needle = radial_line 225 + ((270.0 / @range) * @position), 0..90
end
end
end

Shoes.app do
stack do
para "Enter a number between 0 and 100"
flow do
@p = edit_line
button "OK" do
@s.position = @p.text.to_i
end
end

@s = speedometer :range => 100, :ticks => 10
end
end

It's about 24 megs of libraries on disk when you're done, but you've got multimedia support and a lot of great library support in that space, not to mention a copy of Ruby itself. The download is only 2.5M bare or 6.8M with Video support.

I think it'd be great to build a Twitter Client using Shoes. The race is on! Hopefully we'll be able to get a Twitter Client done before some schmuck writes a Ruby Shoes virus. I for one, will be keeping the Ruby Shoes virus I wrote to myself. Well, not really a virus as it's a self propagating annoyance. But still. :)

It'll be fun to watch how this evolves. I hope there will be a clean upgrade process. If you want to try running your first Ruby/Shoes app, then run this EXE if you've got Windows or this DMG if you're on a Mac to see a basic demo application, but more importantly, to experience in the installer/bootstrapping process.

Technorati Tags: ,,

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Tuesday, June 24, 2008 7:56:41 AM UTC
Actually, you don't need a seperate ruby installed in order to run Shoes. Shoes comes with Ruby built in. Just install Shoes and you're set.

About that twitter client: I developed one with a friend of mine. It's on hold for the moment, as it did the bulk of it's updates through IM and not through the API. It's remarkable how quick you get addicted to continuous updates instead of every 6 seconds or so. But since twitter IM is down, we're not using (and developing) it for the moment.

The source lives in github, I'll check with my friend if it's okay to open it up.
Tuesday, June 24, 2008 7:57:36 AM UTC
I was one of the (un)lucky ones that attended NDC2008 and saw you keynote. Lucky in the sense that it was a great conference and your keynote rocked. Unlucky since we all nearly died from the stuffed and hot air! Did you enjoy the conference Scott?

Oh, and Ruby is pretty cool, though I'm not 100% friends with the code just yet...
Tuesday, June 24, 2008 10:53:54 AM UTC
Shouldn't they be 'slippers' instead of 'shoes' as in: [http://en.wikipedia.org/wiki/Ruby_slippers]
Tuesday, June 24, 2008 12:35:01 PM UTC
I've been loosely following Rebol for years and you are the first person I've seen mention it in the .NET world. I'd be curious to hear your thoughts on it and why it hasn't taken off as a language like Ruby. I know it's a young language, but . . .
Chris Cyvas
Tuesday, June 24, 2008 5:53:57 PM UTC
Ronnie - I think that he's calling it Shoes is the joke! ;)

Chris Cyvas - I think it hasn't succeeded for the same reason that Java on the desktop didn't succeed. It looks weird. I know it's an odd thing to say, but seriously, it's a weird looking UI. It doesn't look native, not even close. Java had the same problem for years and by the time they figured it out, it was too late. The brilliance of the language can't be seen behind the "meh" of the UI toolkit.
Tuesday, June 24, 2008 6:17:44 PM UTC
A great post about Shoes. Thanks for the link; that particular project has languished (so far) ever since I started it. It would now need yet another fresh start, but maybe that's not a bad idea, with all the new features constantly being added to Shoes.

Tuesday, June 24, 2008 7:44:41 PM UTC
Ruby is the bomb ! I just got started on this. InstantRails reduces to TFA (time to first app) to almost nothing. I don't know if Shoes are in there.

It is ridiculously easy to create speedometer/dial control using WPF also. Check it out here - dial-gauge-is-a-progressbar (www.tewari.info)
Tuesday, June 24, 2008 9:27:04 PM UTC
Good to see a post about Shoes. I've been blogging about learning Ruby and I just blogged the beginnings of a Twitter client using Shoes here.
Tuesday, June 24, 2008 9:39:27 PM UTC
Oh my god Shoes!
Thursday, June 26, 2008 4:37:34 PM UTC
_why is one of the programmers I can reference to bring me down a peg or twenty-three any time I start thinking I'm getting good at this game.
Thursday, June 26, 2008 8:57:52 PM UTC
Why is a genius. Very productive, smart programmer and funny with words (his online Ruby book, I wish I could have that in print!)
Mike
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.