Saturday, May 25, 2013

Rails as a single-page app development environment

Why?

TL;DR: Rails is a great tool to set up a development environment for single-page apps, particularly if you already know the framework. Skip to How to get started.

When writing single-page apps using frameworks like Backbone.js or Ember.js, I found myself frustrated by the lack of development environment, coming from the Rails world where a lot of tools are provided to keep your code clean, organised and optimised.

I tried tools like rake-pipeline and Yeoman, but ultimately I was disappointed, craving a tool that would do just like Rails with its assets pipeline, but without the actual Rails stuff. Until a few days ago, my friend and colleague Kevin interrupted my rant: "Why not Rails"?

Here is what Rails can do for you, and I will show you how:
  • Splitting code into multiple file (readability): There is one eyesore I find common to all demos and tutorials of JavaScript frameworks: app.js. Somehow, learning to build a single-page app always seem to include cramming all of your code into one huge, disorganised, unmaintainable and overall disgusting JS file.
    You can't reasonably work like that, at least not for long. I want each model, each view, etc in a separate file.
  • Using SASS and CoffeeScript: Hardcore JS developers might argue against CoffeeScript, but I can't possibly go back. And I don't think anyone can argue against SASS. It's just awesome.
  • Joining and compressing JS/CSS (performance): If you split your JS and CSS files and then include them all in your app, you need to stop. Each additional link or script means an additional requests, which means more delay before rendering your app. And obviously, your assets need to be minified before hitting production.
  • Updating librairies (maintainability): All the common librairies and frameworks such as Backbone.js, jQuery, Bootstrap and Foundation are available as Ruby gems, which makes installing and updating them a lot easier than browsing all the websites regularly and downloading the new versions.
  • Watching for changes: In development mode, Rails will recompile your assets automatically if you change them, so you can skip between your code and your browser without stopping by the console first.
But isn't it overkill to use a complete web framework just to manage your assets? Maybe a little, but who cares? It's not like your Rails app is ever going to production, so nothing changes on that front, and Rails is as easy to install as the other tools you might find like Yeoman or Compass.

How?

I will assume you have Rails installed, and I will try to make this easy to follow for non-Rails developers, but you might want to read about the asset pipeline if that is the case.

At the time of writing, Rails 4 (RC1) still has some bugs affecting the assets pipeline, so I strongly recommend you stick to Rails 3. I tried to make this Rails 4 ready, but I might have forgotten some details. And besides, we won't use any of the cool new stuff from Rails, so there is zero advantage to living on the edge here.

First, let's create a new Rails app. We add flags to exclude stuff that we won't need, although it wouldn't hurt if it was there. Our built app will go in the public directory, so let's clean it out.
Next we open app/assets/javascripts/application.js, and remove what we don't need. Delete these lines:
We want the resulting files to be called application.js and application.css, but by default Rails adds a digest to that name. Change this line in config/environments/production.rb:
Finally, we can create our index.html. Place it in the public directory:
You can now work on your app like you would on a Rails app, except you will only modify the assets and not do anything in Ruby nor Rails! Start the server with rails server and open localhost:3000 to see that everything is working.

To build you app, just run rake assets:precompile. Everything will be built in the public folder. Remember to run rake assets:clean when you are done, or else Rails will continue to serve the files in public instead of your actual source files.

Tuesday, May 15, 2012

Install MacVim with file browser with brew

I was watching Yehuda Katz's latest talk at RailsConf 2012 (which is great by the way), and I realized he was using a native file browser in MacVim which looked pretty awesome (I don't really like NERDTree).


So I was pretty happy when my friend Yannick pointed me to the project, but I didn't want to compile it by hand. I've really gotten used to brew and, I'll admit, a little lazy. I like when stuff just updates itself, because I would never update it otherwise. Which is why I made the macvim-split-browser brew formula.

Unfortunately it was getting too specific for brew, so I had to make a tap to host it. Here's the link with installation instructions: https://github.com/joelcogen/homebrew-macvimsplitbrowser

Monday, August 29, 2011

Installing Mac OS X Lion on a Clevo M860TU




Known issues
  • Wifi doesn't work, and no driver is available. Use a USB adapter or cable
  • If your sound doesn't work, reinstall AppleHDA.kext from the kit using Kext Helper

Prerequisites
  • A Mac/Hackintosh with (Snow) Leopard (doesn't have to be the computer you want to install Lion to)
  • A USB drive of at least 5GB (and no data)
  • A Lion app or dmg
  • A USB mouse

Preparing your BIOS
First of all, you'll have to make sure your BIOS version supports AHCI for SATA. If it doesn't, you'll have to flash it. This thread may help you, but then you're on your own, sorry.


Preparing the installation disk
  1. Download Kakewalk for Lion, unzip it and launch the Kakewalk app
  2. Choose "Install to USB" and select your Lion app or dmg
  3. Kakewalk will then ask you for your motherboard version. Just keep the first one and finish the install
  4. Download and extract my Lion M860TU kit: GTX 260M (with nvEnabler) - 9800GTS
  5. Open a terminal and run prepare_disk.sh from the kit by typing:
    cd Downloads/Lion_M860TU/
    sh prepare_usb.sh
    Replace the directory by the one you extracted the kit to

Installing Lion
  1. Backup all your data
    I recommend using Time Machine so you can restore easily on your new Lion
  2. Reboot, and make sure to boot on your USB drive (press F1 during boot to choose the startup disk).
  3. Choose your USB drive from the menu (called Kakewalk).
    If you get a Kernel panic here (dark screen with reboot message), power off and start again but type "-x" before pressing enter to choose your disk.
    If you get stuck on the Apple logo and nothing else (no spinner, no disk activity) for a few minutes; just power off the computer and start again.
  4. Plug in your USB mouse as your touchpad won't work, follow the installation instruction
  5. Once the installation is complete, reboot back to the setup (Kakewalk disk again, just like you were doing a new install in steps 2 and 3)
  6. Choose your language, then open a terminal from the Utilities menu, and run
    sh /post_install.sh LIONVOL
    (replace LIONVOL by the name of the volume you installed Lion to, case-sensitive)
  7. Reboot from your USB drive again, but this time choose the volume with Lion installed
  8. Setup your account
    If you restore a Time Machine backup from another Hackintosh, choose what you restore wisely! I had to start over 3 times because of a kext causing a kernel panic...

Installing the bootloader
  1. Once you're in Lion, run Kakewalk from your USB disk, but this time chose "Install to computer". Again, leave the first motherboard selected
  2. Open a terminal and run prepare_boot.sh from the kit by typing:
    cd /Volumes/Kakewalk/
    sh prepare_boot.sh
  3. Eject the USB drive and reboot, your new Lion install should boot automatically

Upgrading to 10.7.1
  • If you installed 10.7.0, you can simply upgrade using Software Update
  • This guide should work to install 10.7.1 directly, but I didn't test it myself

    Please write a comment below if you need any help, or let me know if everything worked!

    Saturday, July 9, 2011

    Minidoudou is back on track

    After neglecting Minidoudou for a while, I am making it my priority project for now! There is still some work to do before it is ready for contributors, so here's a small list of the main changes I'm going to do:
    • Add user authentication with Devise (contributors only, you don't need to login to customize a ROM)
    • Add some info to the ROMs: uploader, link to original release, link to MDD release
    • Add Capistrano for easier deployment
    • Find contributors willing to upload ROMs and configure them
    You can follow the progress on GitHub, and if you want to contribute to the source code (or by uploading a ROM), don't hesitate to comment/e-mail/tweet/poke.

    Hopefully we will soon see a lot of [MDD] posts on XDA!

    Converting bzr repositories to git

    After using Bazaar for some projects and Git for others, I decided to stay on Git, mainly for speed and github.

    Here's what I did to convert my bzr repositories to Git, keeping history:

    Install bzr-fastimport from your package manager or, if you're not on GNU/Linux, download it from http://wiki.bazaar.canonical.com/BzrFastImport.

    Create your new Git repository

    Copy the history

    Do a checkout to get the files. Git will act like nothing happend, but ls before and after will tell you otherwise

    If you used bzr ignore, rename the list so it will be used by Git

    You could do a commit to mark the change. If you didn't rename .bzrignore, you'll have to force it:

    If this isn't a one-shot conversion and you need to update the Git repository again, you can use marks so only new changes are exported. Read the comments on this page for a short tutorial.

    Thursday, September 23, 2010

    forward / method_missing with Cappuccino

    Cappuccino allows to forward unhandled messages to another object, similarly to Objective-C's forward and Rails' method_missing functionalities.
    However, I couldn't find any documentation on this matter, which is why I found useful to detail it a bit here. As all this information comes from message boards and mailing lists, please forgive me if some details are missing or unclear, and feel free to comment if you have deeper insight on the subject.

    What is invocation forwarding?

    Invocation forwarding allows an object to forward unsupported invocations to another object. In simpler terms, let say you have an object with no methods, we'll call it empty_obj, but which has forwarding set to another object, working_obj (we'll see how in a minute). When you call [empty_obj doSomething], you won't get an error like you would expect, but instead the return value of [working_obj doSomething].
    This might seem useless, but can actually prove quite necessary when you use wrapper classes for instance, that should forward most methods to another object.

    Let's see some code!

    Let's declare the SomeWrapper class, which does nothing but include a SomeClass object.

    Except for its pretty standard init: method, this class does nothing. However, all method calls will be forwarded to o, so our wrapper will look like it implements all the methods from SomeClass.
    You probably guessed that it's all thanks to the implementation of methodSignatureForSelector: and forwardInvocation:, so let's see what each does and how you can use them to match your needs.

    - (CPMethodSignature)methodSignatureForSelector:(SEL)selector

    This method is used to determine if a selector can be sent to an object. Here, we say YES no matter what, so we delay the error to the second invocation (in forwardInvocation:).
    You could also check what the selector is, for example to hide certain features of SomeClass. Doing so is as simple as comparing two strings (hint: one of them is selector), and return YES or NO to accept or revoke the call, respectively.

    - (void)forwardInvocation:(CPInvocation)anInvocation

    As you probably know by now, this is where the magic occurs. The anInvocation parameter is the invocation that was sent to this object. Being itself an instance of the CPInvocation class, it can me manipulated in lots of ways.
    Let's have a look at its three most interesting methods: invokeWithTarget:, setReturnValue:, and setSelector:.
    invokeWithTarget:, which is the method we used in our example, invokes the same selector, with the same arguments, on another target. This is the basic form of forwarding.
    setSelector: is pretty self-explanatory, it will change the selector used the next time you call invoke on the object (probably at the next line). This means you can accept a selector, but forward another, which offers endless possibilities.
    setReturnValue: will, well, change the return value. So now, you can have an class that returns a specific value, no matter the method invoked, simply by changing the invocation to [anInvocation setReturnValue:42] !

    Of course, you can retrieve the target and selector, as well as manipulate arguments. See the Cappuccino API page for CPInvocation.

    Using attributes instead of methods

    As far as I know, you cannot use the forwarding mechanism with attributes. This seems to make sense, as attributes are a JavaScript feature where methods are an Objective-J enhancement.
    However, what you can do is convert a method invocation to fetching an attribute. Here is how it should look like:

    Instead of invoking our method on o, we retrieve the selector, use it as a parameter that o should have, and use the result as the return value.

    No return!

    You might find this CPInvocation handling is quite a hassle. In my setReturnValue: example, why didn't I simply use return 42 instead of calling a method on anInvocation.
    Well, the answer is simple: forwardInvocation: has a return type of void, because anything you return will be lost. It is not called by Objective-J in lieu of the "real" method (the one you called but doesn't exist) the way you regularly call methods, and changing the return value won't help, either. No big deal, but pay attention to it.

    However, the forward: method works in a way more similar to Objective-C, but you'll have to check the source code to see exactly how that works.

    I hope you learned a powerful tool today, one that was cruelly missing from JavaScript.