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

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

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

Add Jtalk to your application

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

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:

<!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>

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:

increase
count := count + 1.
header contents: [:html | html with: count asString]

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:

rails generate controller uploader jtalk

And edit config/routes.rb as follows:

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

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:

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

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:

smalltalk.addClass('Myclass', smalltalk.Object, [], 'Mycategory');

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.

About these ads

Manage your libraries with symlinks on Windows

My Windows development environment is a bit complex. I work on multiple projects, at multiple versions, with different compiler environments, and with dependencies on different libraries and versions of these libraries.

Now, how do I specify where the compiler must search the libraries, and which version to use? Most of the time, I will add it directly in the project configuration. Easy at first, but quickly,I find myself writing by hand the path to each library (and header) in each project and each configuration of this project. And then, writing by hand the different names of the libraries (mylibrary.lib, mylibraryd.lib, mylibrary-static.lib, mylibrary-MTD.lib, etc).

And when I want to update to a new version of the library? If I’m lucky, I just have to change the library and header paths (in every project using the library). If not, I also have to change the name, because of the library developer’s convention.

The first solution to these problems was to use a batch file to launch Visual Studio and MSYS, and set some environment variables in this file. I quickly ended up with one big file containing two environment variables (include path and lib path) per library, possibly more if there were some big changes in the library names. My Visual Studio configuration was cluttered with $(MYLIBRARY_LIBPATH), $(MYLIBRARY_INCLUDEPATH), $(MYLIBRARY_NAME). It is unreadable, and again, impossible to maintain.

My solution comes from the Unix world, where you have a correct organization for your development files:

  • one folder containing the subfolders include, bin and lib
  • library names including version, and a symlink (without the version number) to the latest version of the lib

Can I do that on Windows? YES \o/

Here is the trick: normal links on Windows won’t work, but the mklink tool can create symlinks. And Visual Studio will recognize those as files and folders while looking for libraries.

Now, how would I organize my development environment? I chose to use (and abuse) symlinks, to create include, lib and bin folders for each project and configuration, and use generic names for the libraries.

  • I create a folder containing include, lib and bin
  • in the  include/ folder, I put symlinks to the header file or the subfolder for each library I will use in that project
  • in the lib directory, I create symlinks to the library version I want, one symlink per static/dynamic, MT/MD, Debug/Release version. But I could create one lib folder per static/dynamic, etc. A bit complex, but feasible (most of the time, I use only debug and release version, so it’s still manageable).

With this setup, I only set the INCLUDE and LIB environment variables, and I use directly the library names I need.

Here is an example script I use to create different library folders for x86 and x64 libs:

echo "Building include and library directories for Windows %PLATFORM%"

@mkdir %PLATFORM%
@mkdir %PLATFORM%\include
@mkdir %PLATFORM%\lib

@mklink /D %PLATFORM%\include\boost %BOOST%\boost
@for %%i in (%BOOST%\lib\*.lib) do (mklink %PLATFORM%\lib\%%~ni.lib %%~fi)

@mklink /D %PLATFORM%\include\cpptest %CPPTEST%\include\cpptest
@for %%i in (%CPPTEST%\lib\*.lib) do (mklink %PLATFORM%\lib\%%~ni.lib %%~fi)

I set up the BOOST and CPPTEST environment variables in another file. Then, I launch Visual Studio from another script which includes it.

There may be better ways, and that system will evolve in the future, but I’m pretty comfortable with it right now :)

Depending on my needs, I may grab from the bottom of my disk the package manager I wrote back in school, and make a big solution to download, build and link libs and personal projects. But later, I have some procrastination planned right now.