Jtalk on Rails: editing Javascript in my browser

If you don't know JTalk yet, you're missing something. It's an awesome piece of work: a Smalltalk to Javascript compiler and a Smalltalk editor running in Javascript, IN YOUR BROWSERGo check it out, now!

Now that you've played a bit with JTalk, let's get started.

If you're like me, you're a bit annoyed by WebDAV, the proposed solution to save changes to disk. And if you're like me, you would like to use Jtalk with Rails, and because you're a lazy ass like me, you use WEBrick instead of Apache for your development.

Let's hack something up to replace WebDAV!

Create a Rails application

[sourcecode language="bash"]
rails new jtalkonrails
cd jtalkonrails
bundle install
rm public/index.html
rails generate controller home index
printf "Jtalkonrails::Application.routes.draw do\n  root :to => \"home#index\"\nend\n" > config/routes.rb
[/sourcecode]

(I should really make a script out of all my rails initialization commands, one of these days...)

Add Jtalk to your application

[sourcecode language="bash"]
cd public/
wget http://github.com/NicolasPetton/jtalk/tarball/master --no-check-certificate
tar zxvf master
cp -R NicolasPetton-jtalk-20cd63e/st .
cp -R NicolasPetton-jtalk-20cd63e/js .
cp -R NicolasPetton-jtalk-20cd63e/css .
cp -R NicolasPetton-jtalk-20cd63e/ide .
rm -rf NicolasPetton-jtalk-20cd63e
[/sourcecode]

JTalk stores source code in three forms: Smalltalk code, Javascript code and smaller Javascript code ("*.deploy.js").

Jtalk hello world: the Counter example

now, edit app/views/layouts/application.html.erb so that it looks like this:

[sourcecode language="html"]
<!DOCTYPE html>
<html>
<head>
<title>Jtalk On Rails</title>
<%= stylesheet_link_tag :all %>
<%= javascript_include_tag :defaults %>
<%= csrf_meta_tag %>
<script src="js/jtalk.js" type="text/javascript"></script>
<script type="text/javascript"> loadJtalk()</script>
</head>
<body>
<button onclick="smalltalk.Browser._open()">Class browser</button>

<div id="counters"></div>

<script type="text/javascript">
jQuery(document).ready(function() {'#counters'._asJQuery()._append_(smalltalk.Counter._new())});
</script>
<%= yield %>

</body>
</html>
[/sourcecode]

Here, we included a button to open the code browser, and added a Counter in a div. Oh, I forgot to tell you: Jtalk works seamlessly with JQuery :)
Now, go check it out, and you will seee the counter and be able to increase and decrease the value displayed (yes, that's a counter).

Editing the code

Click on the "Class browser" button to start the IDE. Select the "Examples" category, the "Counter" class, the "actions" method category, and the "increase method". You will see in the text box below the source code of the increase method:

[sourcecode language="smalltalk"]
increase
count := count + 1.
header contents: [:html | html with: count asString]
[/sourcecode]

Edit that method to increase by steps of 2 instead of 1, and hit "Save". Now, the counter on your page will increase by steps of 2.
Unfortunately, on the next page refresh, you will lose these changes. That's why the "Commit category" button is there.
It will take the updated files (here, Examples.st, Examples.js and Examples.deploy.js) and make a PUT request to their original URL.

A PUT, you said? Well, I can work something out with a PUT.

Saving the code

Let's create a new controller, called Uploader:

[sourcecode language="bash"]
rails generate controller uploader jtalk
[/sourcecode]

And edit config/routes.rb as follows:

[sourcecode language="ruby"]
Jtalkonrails::Application.routes.draw do
root :to => "home#index"
if Rails.env == 'development'
put 'st/:id' => 'uploader#jtalk'
put 'js/:id' => 'uploader#jtalk'
put 'js/:id.:deploy' => 'uploader#jtalk'
end
end
[/sourcecode]

Now the PUT requests are redirected to our controller, but only in the development environment. You do not want to make your JS editable from the browser in a production app. DO NOT WANT!

The only thing left is the controller itself:

[sourcecode language="ruby"]
class UploaderController < ApplicationController

def jtalk
path = Rails.root.join('public')
if(params[:format] == "js")
path = path.join("js")
elsif(params[:format] == "st")
path = path.join("st")
end

if(params[:deploy])
path = path.join(params[:id]+".deploy."+params[:format])
else
path = path.join(params[:id]+"."+params[:format])
end

File.open(path, "w") do |f|
f.write(request.body.read())
end

head 200
end

end
[/sourcecode]

Here, we build the file path from the parameters. I use request.body.read() to get the file content because Rails seems to truncate the beginning of the file.

Profit

Now, go back to the web page, click on "Commit category", and refresh the page. Your changes were saved! You can enjoy editing your frontend directly from the webpage itself, in the code browser, and more importantly, write your whole frontend in Smalltalk! It's still missing the workflow "edit-try-debug-edit-continue", but it already feels just like a "normal" Smalltalk environment. It feels like home :)

Post Scriptum

If you want to add a new category, it's easy: create a file Mycategory.js and put it in public/js, with this content:

[sourcecode language="javascript"]
smalltalk.addClass('Myclass', smalltalk.Object, [], 'Mycategory');

[/sourcecode]

and change your initialization from loadJtalk() to loadJtalk(new Array("Mycategory.js")). The new category will now appear in the code browser, and clicking on "Commit category" will create the deployment file and Smalltalk source file.