Category: Technical

Posts related to programming or other highly technical stuff that can only be of interest to computer geeks.

  • I’m reading the book Refactoring and one of the refactorings it shows is called “Consolidate Duplicate Conditional Fragments” and it shows an example in Java:

    if (isSpecialDeal()) {
      total = price * 0.95;
      send();
    } else {
      total = price * 0.98;
      send();
    }

    is refactored into

    if (isSpecialDeal()) {
      total = price * 0.95;
    } else {
      total = price * 0.98;
    }
    send();

    If you do it in Python it’s actually quite similar:

    if isSpecialDeal():
      total = price * 0.95
      send()
    else:
      total = price * 0.98
      send()

    is refactored into

    if isSpecialDeal():
      total = price * 0.95
    else:
      total = price * 0.98
    send()

    But in Ruby it’s different. In Ruby, like in Lisp, everything is an expression, everything has a value (maybe there are exceptions, I haven’t found them). Let’s look at it in Ruby:

    if isSpecialDeal()
      total = price * 0.95
      send()
    else
      total = price * 0.98
      send()
    end

    is refactored into

    total = if isSpecialDeal()
      price * 0.95
    else
      price * 0.98
    end
    send()

    Or if you want it indented in another way:

    total = if isSpecialDeal()
                  price * 0.95
                else
                  price * 0.98
                end
    send()

    We can push it one step further:

    total = price * if isSpecialDeal()
                              0.95
                            else
                              0.98
                            end
    send()

    Of these three languages, only Ruby can manage to have inside each branch of the if only what changes depending on the condition and nothing else. In this simple case you could use the ternary operator, :?, but if the case wasn’t simple, Ruby would be at an advantage.

    I’m reading Refactoring: Ruby Edition next.

  • I strongly believe in revision control. Back in the days when there wasn’t any other choice that CVS I spent countless hours setting up CVS servers and re-freshing my mind on its obscure commands and arguments, which were almost all of them. If you drop my copy of Professional Linux Programming it’ll open on the CVS chapter by itself (much like my copy of The C Programming Language will open itself on the pointers chapter, somewhere on the pointers to pointers section).

    Then came Subversion and it was somewhat better. After that we got the explosion of revision control systems with the introduction of distributed ones. We’ve got Darcs, Git, Mercurial, Arch, Bazaar, etc. My choice right now is Git. It used to be Darcs, but unfortunately it stagnated for a long time and I moved on to the next new thing. I’ve used Mercurial as well. From my perspective Mercurial and Git are almost equivalent.

    For me distributed revision control was a breakthru. There’s one small feature that makes a huge difference for me. In the old days I used to set up CVS or Subversion servers/repositories (in those, a repository is essentially a server-side thing). I had to decide where that repository was going to reside, how it was going to be named, how it was going to be accessed (ssh, http, custom protocol?), by whom, etc.

    Today with Git I just do this

    git init

    That’s it. I’m done. The directory where I am is now a repository. I can start committing, branching, rolling back, looking at the history, generating patches, stashing, etc. It’s that simple. The fact that it’s so simple goes from a quantitative advantage to a qualitative advantage. What I mean is that I not only revision-control things faster but that I revision-control things I wouldn’t have otherwise. For example the configuration directories on my servers.

    I know many people will complain: “But with no centralized repository chaos will ensue”. The fact that it’s distributed doesn’t mean there can’t be a central repository. When I want to collaborate with someone using Git I create one repo somewhere, in my servers or GitHub and we both push and pull from there. The difference is that I commit a lot locally, and when the chances are ready I push them. That means that I can commit broken code, without worrying.

    At work we don’t use a distributed revision control system, we use a centralized one and we have a very string peer reviewing policy. My current tasks involve touching code in many different files in many different systems never getting familiar to any of them. That means that it’s common for my peer reviews to say things like “all this methods don’t belong here, these two should go there, that one should be broken into three different ones going here, there and somewhere else”.

    Now I have a problem. I can’t commit because my peers don’t consider my code ready. My code works but it has to be refactored in a very destructive way. What happens if during the refactoring it stops working. For example copying and pasting I loose a piece of code. I can’t roll back to my working state and start over. If we were using a distributed revision control system I could.

    So, being able to commit non-finished code locally while colaborating with other people is one of my other crucial features in DVCS.

    The third one is being able to branch locally. In a similar vein as the last example. When I find myself thinking about a very destructive refactoring that I’m not sure if it’s going to get me anywhere and worst than that is going to take me three days to do; I just create a local branch. I experiment in that branch. If at any time I get tired or I need to do anything else I go back to the main one. That is what branching is about.

    Why is locally branching better than globally or centralized branching? Well, one reason is that a local branch doesn’t have to make sense to anyone else. I don’t have to pick a name that’s descriptive for anyone else than me. I don’t have to justify myself for creating a branch with anyone else. Let’s suppose I had an argument with a co-worker where I believe something is doable and (s)he believes is not. Do I want him/her to see that I created a branch to prove him/her wrong? I don’t. And if I prove myself wrong I want to quietly delete that branch and never ever talk about it.

    But I am starting to go into the very hypothetical realm. In short, for me, DVCS is about this:

    git init

    and get to code.

  • I like Erlang and I like gen_servers, I end up coding a lot of stuff as gen_servers and I like behaviors in generally. I even wrote some myself. I haven’t used Erlang for a while. I’ve been playing with it in the last couple of days, hopefully I’ll announce something soon.

    I ended up creating my own gen_servers template to get started. It’s a very verbose one and I’m putting it here more for me to know where it is that anything else; but, maybe it’s of use for someone else:

    -module(module).
    -compile(export_all).
    
    -behaviour(gen_server).
    -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
    
    %% Public API
    
    start() ->
      gen_server:start({local, ?MODULE}, ?MODULE, [], []).
    
    stop(Module) ->
      gen_server:call(Module, stop).
    
    stop() ->
      stop(?MODULE).
    
    state(Module) ->
      gen_server:call(Module, state).
    
    state() ->
      state(?MODULE).
    
    %% Server implementation, a.k.a.: callbacks
    
    init([]) ->
      say("init", []),
      {ok, []}.
    
    
    handle_call(stop, _From, State) ->
      say("stopping by ~p, state was ~p.", [_From, State]),
      {stop, normal, stopped, State};
    
    handle_call(state, _From, State) ->
      say("~p is asking for the state.", [_From]),
      {reply, State, State};
    
    handle_call(_Request, _From, State) ->
      say("call ~p, ~p, ~p.", [_Request, _From, State]),
      {reply, ok, State}.
    
    
    handle_cast(_Msg, State) ->
      say("cast ~p, ~p.", [_Msg, State]),
      {noreply, State}.
    
    
    handle_info(_Info, State) ->
      say("info ~p, ~p.", [_Info, State]),
      {noreply, State}.
    
    
    terminate(_Reason, _State) ->
      say("terminate ~p, ~p", [_Reason, _State]),
      ok.
    
    
    code_change(_OldVsn, State, _Extra) ->
      say("code_change ~p, ~p, ~p", [_OldVsn, State, _Extra]),
      {ok, State}.
    
    %% Some helper methods.
    
    say(Format) ->
      say(Format, []).
    say(Format, Data) ->
      io:format("~p:~p: ~s~n", [?MODULE, self(), io_lib:format(Format, Data)]).
    

    Update 2010-01-04: I keep changing the actual contents of the template as I’m coding a little Erlang app. I’ve recently added, among other things, a call to get the state of the process, useful for debugging.

  • First and foremost I’m a coder, a coder who strongly believes in revision control. Second I am also a sysadmin; but only by accident. I have some servers and someone has to take care of them. I’m not a good sysadmin because I don’t have any interest on it so I learn as little as possible and also because I don’t want to be good at it and get stuck doing that all the time. What I love is to code.

    I’m always scare of touching a file in /etc. What if I break something? I decided to treat /etc the same way I treat my software (where I am generally not afraid of touching anything). I decided to use revision control. So far I’ve never had to roll back a change in /etc, but it gives me a big peace of mind knowing that I can.

    In the days of CVS and Subversion I thought about it, but I’ve never done it. It was just too cumbersome. But DVCS changed that, it made it trivial. That’s why I believe DVCS is a breakthru. Essentially what you need to revision-control your /etc with Git is to go to /etc and turn it into a repository:

    cd /etc
    git init

    Done. It’s now a repo. Then, submit everything in there.

    git add .
    git commit -am "Initial commit. Before today it's prehistory, after today it's fun!"

    From now on every time you modify a file you can do

    git add <filename>
    git commit -m "Description of the change to <filename>"

    where <filename> is one or many files. Or if you are lazy you can also do:

    git commit -am "Description to all the changes."

    which will commit all pending changes. To see your pending changes do:

    git status

    and

    git diff

    When there are new files, you add them all with

    git add .

    and if some files were remove, you have to run

    git add -u .

    to be sure that they are remove in the repo as well (otherwise they’ll stay as pending changes forever).

    That’s essentially all the commands I use when using git and doing sysadmin. If you ever have to do a rollback, merge, branch, etc, you’ll need to learn git for real.

    One last thing. I often forget to commit my changes in /etc, so I created this script:

    #!/usr/bin/env sh
    
    cd /etc
    git status | grep -v "On branch master" | grep -v "nothing to commit"
    true # Don't return 1, which is what git status does when there's nothing to do.

    on /etc/cron.daily/git-status which reminds me, daily, of my pending changes.

    Hope it helps!

  • I like knowing when something goes wrong with my web apps, so I’m using Super Exception Notifier to get by email a report should any exception be raised in the app. If you go to Super Exception Notifier’s site you’ll see some instructions on how to add it to your project. This is how I do it.

    Add the gem requirement in environment.rb:

    config.gem 'super_exception_notifier', :version => '~> 2.0.0', :lib => 'exception_notifier'
    

    Then be sure to have gemcutter in your gem sources:

    gem sources
    *** CURRENT SOURCES ***
    
    http://gemcutter.org
    http://gems.rubyforge.org/
    http://gems.github.com
    

    If you don’t have it, you can add it this way:

    gem install gemcutter
    gem tumble

    To install the gem, in your Rails project run:

    sudo rake gems:install

    Create a file in config/initializers, I’ve called it exception_notifier.rb and inside I’ve set up the only really needed value for the notifications, the email address:

    # Notification configuration
    ExceptionNotifier.configure_exception_notifier do |config|
      config[:exception_recipients] = %w(pupeno@pupeno.com)
    end
    

    The last task is to make your application controller noisy by adding one line to it (the second one of course):

    class ApplicationController < ActionController::Base
      include ExceptionNotifiable
      #...
    end
    

    You also need to be sure that you have ActionMailer properly configured, otherwise no mail is going to get through.

  • For my Ruby on Rails pet project Is it Science Fiction? I’ve reached that point when I wanted to show tabs. You know, for the menu, on top of the page. I quickly wrote something like:

    <ul class="tabs">
      <li><%= link_to 'Home', root_url %></li>
      <li><%= link_to 'Recommend', new_item_url %></li>
      <li><%= link_to 'Ranking', items_url %></li>
      <% if session[:user_name] -%>
        <li><%= link_to 'Log out', session_url, :method => :delete %></li>
        <li><%= link_to 'My Profile', edit_profile_url %></li>
      <% else %>
        <li><%= link_to 'Log in', new_session_url %></li>
      <% end %>
    </ul>
    

    and styled it like the Listamatic Unraveled CSS tabs. And everything was good, except that the current tab wasn’t highlighted. Highlighting it is a matter of setting the correct class for it.

    For that I created a new version of link_to that adds the “current” class to a link if it points to the current page. That is not very hard actually, but you have to consider that some tabs, like Ranking, are not only highlighted when that page is show, but when any subpage (like a ranked item) is shown as well. After trying many solutions (in an effort to find the simplest one) I’ve settled for link_to2 and the tabs now look like this:

    <ul class="tabs">
      <li><%= link_to2 'Home', root_url %></li>
      <li><%= link_to2 'Recommend', new_item_url %></li>
      <li><%= link_to2 'Ranking', items_url, {:extra_current => {:controller => :items, :action => :show}} %></li>
      <% if session[:user_name] -%>
        <li><%= link_to 'Log out', session_url, :method => :delete %></li>
        <li><%= link_to2 'My Profile', edit_profile_url %></li>
      <% else %>
        <li><%= link_to2 'Log in', new_session_url %></li>
      <% end %>
    </ul>

    Look at the Ranking tab, it has an extra_current that adds other pages to be treated as current. The code to do this is the following (I’ve put it in application_helper.rb):

    module ApplicationHelper
      def link_to2(*args, &block)
        if block_given?
          options      = args.first || {}
          html_options = args.second || {}
        else
          name         = args.first
          options      = args.second || {}
          html_options = args.third || {}
        end
    
        if current_page?(options) or as_array(html_options[:extra_current]).any? {|o| current_page2? o}
          html_options[:class] = add_class(html_options[:class], "current")
        end
    
        html_options.delete(:extra_current)
    
        if block_given?
          link_to(options, html_options, &block)
        else
          link_to(name, options, html_options, &block)
        end
      end
    
      private
    
      def add_class(classes, new_class)
        ((classes or "").split(" ") << new_class).join(" ")
      end
    
      def as_array(o)
        if o == nil
          []
        elsif not o.is_a? Array
          [o]
        else
          o
        end
      end
    
      # current_page? of {:controller => :blah, :action => :bleh} when the routes also require an id raises a route error. current_page2? doesn't.
      def current_page2?(p)
        current_page? p
      rescue
        return false
      end
    end
    
    

    Feel free to pick the code and use it in any way you want. I’m thinking of turning it into a gem, but I need a better name than link_to2, any ideas?

  • Ruby on Rails has a special object called flash which hold its contents for one more request. It’s particularly useful to show messages after a redirect. Since it’s good style to redirect after each succesful form post, that’s where you put the messages such as: “You’ve logged in”, “Thank you for your feedback”, “The book has been added”, etc.

    This flash object looks like a hash table. I normally use two items in there: notice, for good stuff and errors, for bad stuff. I want these messages to be displayed in all pages, so whenever something bad or good happens, I just drop stuff in the notice or error and forget about it. This is trivial to do, I just put this:

    <% if flash[:error] -%>
      <p class='error'><%=h flash[:error] %></p>
    <% end -%>
    <% if flash[:notice] -%>
      <p class='notice'><%=h flash[:notice] %></p>
    <% end -%>

    on the application layout.

    The problem with doing that is that it doesn’t look nice. I expect error messages and success messages to be near the forms or UI elements I’m interacting with. That means that every view or page will put it in a different location. No problem, you just add that same snippet where you want the messages to appear.

    Then there’s a second problem: you get messages twice. If you remove them from the application layout, then you have to remember to put in absolutely every view, even those that you don’t expect to never show an error or notice. I don’t trust myself to always remember to do anything, so what happens is that I can’t just drop something in the flash and expected it to be show, I have to check every time.

    I’m doing this in for a pet projects with less than 10 views, but I think big. I think of the project having 100 views and three coders than don’t know all the implicit rules, like adding the display message snippet to every view they create.

    I’ve came up with this solution. I created a partial view with this content:

    <% if not @messages_rendered -%>
      <% if flash[:error] -%>
        <p class='error'><%=h flash[:error] %></p>
      <% end -%>
      <% if flash[:notice] -%>
        <p class='notice'><%=h flash[:notice] %></p>
      <% end -%>
    <% end -%>
    <% @messages_rendered = true -%>

    That partial view is rendered from the views and also from the application layout, but it displays the messages only once. Thankfully Rails renders the partials inside the views first, so that the messages gets displayed according to the view, and if the view didn’t display them, the application layout will.

  • This can unleash so much hate mail, but here it goes, my inbox is ready!

    Are dynamic languages just a temporary workaround? I’m not sure! I’m switching between the two types of languages all the time: Java, Python, C#, JavaScript. I’ll try to make the long story short.

    Statically typed languages, like Java and C#, are nice because when you do

    blah.bleh()

    you know that blah’s class has a bleh method, at compile time. But better than that, when you typed “blah.” you get a list of methods, and you already know whether there’s a bleh method or not, and if you typed bleh and it doesn’t exist, the IDE lets you know, no need to wait for the compiler. Also you can do very deterministic refactoring, renaming all “bleh” for “bluh” for example.

    Statically typed languages are not nice because they are very verbose and require a lot of boilerplate (if you’ve used Haskell, just bear with me for now please), so you end up with things like:

    List[Car] cars = new List[Cars]();
    foreach (Car car in cars) {
        car.Crash();
    }

    How many “cars” do you read there? And that’s a nice example. There are worse. So come dynamically typed languages and you can write:

    cars = []
    for car in cars:
        car.crash()

    You have less cars, and less (no) lists. That means you are more productive. You start chunking out code faster without having to stop and think “What type of object will this or that method return?”. But crash() can crash your application instead of just the car because you can’t know if it exists until run-time. That might be OK or not, testing and whatnot.

    Then comes C# 3.0 where you can do:

    var cars = new List[Car]();
    foreach (var car in cars) {
        car.crash();
    }

    And you can see that syntactically it got closer to Python, which is what gives you the productivity. Don’t know the type? type “var”. But semantically, it’s still statically typed, like the previous statically typed example. You know that car is going to be of some class that has a crash method. You can actually know car’s class at compile time, no need to run it.

    That’s called type inference. You don’t have to specify the type where the compiler is capable of inferring it for you. C# type inference system is still very limited (but better than Java’s). Let’s see an example in another language

    cars = []
    map crash cars

    That means, create a list called cars, call the function crash on each car. Would you say that that is a statically typed language? or a dynamic one? I’d say it is dynamic, but it is static. Very static. It’s Haskell. Haskell’s compiler will infer all the types for you. It’s amazing, you’ll write code as robust as with C#, but as terse as Python’s (Monads will then kill your productivity, but that’s another story).

    In Python 3 you can define types for arguments. They are mostly useless, but it’s an interesting direction. I think the best it can do is that when a program crashes it’ll tell you: “function blah expected an int, but got a float, not sure if that was the problem, but you might want to look into that”.

    Now, my question is, are dynamically typed languages just a temporary workaround? As our compilers get better, our computers faster, will statically typed languages keep giving us as many or more reassurances about our code and utilities while at the same time they become as simple and terse as dynamically typed languages? Or will dynamically typed languages start to gain types and over time be more static without the programmers that use them ever noticing?

    My question is, will we in the future, 50 or 100 years, look back and said “Dynamically typed languages where a temporary workaround to statically languages being painful to use when human beings and their toy computers were so primitive?” in the same way we can say today that “non-lexical scope was a limitation we had and have due to the limitations of computer hardware 30 years ago”.

    Reviewed by Daniel Magliola. Thank you!

  • other-doorOf all the bad practices I see on the web this ranks as very bad and I believe it’s not mentioned enough. It’ll easily make it to my personal top 5.

    I go to a web site, like example.com, and I immediately get redirected to an ugly URL beast, like example.com/news/today?date=2009-06-30&GUID=5584839592719193765662.Wha? Why? First, the site broke any chance I had of making a bookmark of it with just one click. I don’t want to bookmark yesterday’s news (look at the URL, it has a date), and what’s that GUID? Oh well, I go and make the bookmark, pointing to example.com, by hand, because I have no other way.

    Even if it only redirected me to example.com/news/today it’d be pretty bad. That URL may not work tomorrow due to changing software. Or what can be even worse: the software and the content get revamped, the URLs changed and everything is cool again, and since the developers are smart people they leave old URLs working. So my bookmark works, but shows obsolete information.

    With my crazy browsing habits (open a trillion tabs, fast, fast, faster) I go to a page, leave it loading, and when I go back and see a weird URL I end up wondering whether I accidentally clicked on something or something weird happened. I have to go back and check.

    It gets even worse when the URL is rather obscure. My e-banking site has this issue. I go to the bank home page where I can find the e-banking link. I click it and it opens the e-banking page, which sells you the service and in a small corner has a link to the real e-banking application where you can log in and see the big red numbers. I’d say they have a deeper problem than redirecting. They see the bank as a company with its useless propaganda home page and e-banking as a product with its useless propaganda home page and then, the actual e-banking site, somewhere else. They should just have the log in on their home page, like any other on-line service. But I digress.

    Back to redirecting. I click log in and it opens, in another window, a web site with a URL that is measured in meters. Long, ugly and scary. I never even thought of bookmarking that because I’m sure it won’t work the second time. So my bookmark is to the previous page. Just today, after a year of using it, I discovered that there’s a nice short well-formed URL for the log in page, something like: bank.com/ebanking/login which immediately redirects to the ugly one. Thanks to the amazing speeds of Switzerland internet connection and today’s browsers I never noticed.

    If the bank had just been serving the content through that URL, they would have saved more time over a year than it took me to write this post. Literally. I can’t understand why they don’t do it properly. If they are passing session information, they should use session state on the server side and a cookie. If they have a modular structure where the app is located elsewhere, instead of redirecting you they should use a reverse proxy. It takes a day to configure Apache for such a thing if you don’t know what you are doing.

    I’ve been using it for ages to serve Plone sites that are in a subdirectory in a Zope web server which runs in an alternate port, yet the front end is Apache and you are never redirected anywhere. You go to example.com which hits my Apache server and inside makes a request to zope.example.com:8080/example.com and serves you the result, you never leave example.com. Even if you go to the secure version, the SSL part is handled by Apache since Zope is not that good (or wasn’t) at it.

    There are cases to redirect someone on a web site. When the content is no longer available or temporarily unavailable. When the user just submitted a form, you redirect if the form was successfully processed to another page that shows the result of the form (the record created or whatever). There are many reasons to do that but that’s for another post.

    There’s no reason to redirect on load. Please, don’t do it.

    Reviewed by Daniel Magliola. Thank you! Use Other Door picture by cobalt123.

  • NetBeans could make the Ruby on Rails experience great for the vast majority of developers who are using Windows, where installing Ruby, Rails, PHP, MySQL, Python, etc is always a pain and the end result is ugly. But it falls short in some important ways which turned my experience with it into a nightmare.

    The reason I say “for developers using Windows” is because I believe that for everybody else, the experience is great already. Or as good as it can be and NetBeans can be an excellent IDE, but not improve the installation and managing experience.

    This is my story, my rant.

    I downloaded the latest NetBeans and installed it. When creating my first Ruby project, I encountered the first problem. Ruby chocked on my username, which was “J. Pablo Fernández”. You could say it was my fault. Windows 7 asked for my name and I typed it. I wasn’t aware it was asking for my username. Even then I would have typed the same, because Windows 7 doesn’t distinguish between usernames and names, and in the 21st century, computers should be able to deal with any character anywhere.

    I know it’s not NetBeans’ fault, it’s Ruby’s. But! Can you imagine a Software Engineer telling Steve Jobs “oh, copying files in a Mac behaves weirdly because it uses rsync and that’s its behavior, you see, it makes sense because…”? Of course Steve would have interrupted: “You’ve failed me for the last time”. The next developer would have patched rsync, trying to get the patch upstream, or creating an alternate rsync or stop using rsync.

    I’ve spent many hours creating another user, migrating to it, which in Windows is like 100 times harder than it should.

    Hours later, as soon as I created a project I got a message saying that I should upgrade gem, Ruby’s package manager, because the current version was incompatible with the current Rails version. By then I had already played with NetBeans’ gem interface telling it to upgrade everything, it should have upgraded gem as well, not just the gems. Every single developer out there running NetBeans must be encountering this error, and indeed there are quite a few threads about it on forums.

    Trying to upgrade gem with NetBeans was impossible. I think what they did to install and upgrade gems in NetBeans is excellent, but failing to upgrade gem itself was a huge drawback. This one was NetBeans’ fault. Neverfear, let’s do it from the command line.

    When doing it from the command line I encountered another error:

    \NetBeans was unexpected at this time.

    Looking around it seems it’s because of the spaces in “Program Files (x86)”. That means that the command line environment for Ruby that NetBeans installs is broken for everybody. I repeat: everybody. The answer: install it somewhere else.

    Well, I have two things to say about it: first, fix the freaking thing, Ruby, gem, whatever. Paths can have spaces and all kind of weirdness. It’s a big world full of people speaking languages that can’t be represented with ASCII and people that believe computers should do our bidding, instead of the other way around. “If I want spaces you better give me spaces, useless lump of metal and silicon”.

    Second, if you know one of your dependencies is broken, try to avoid triggering the broken behavior or at least warn the user about it. “We see you picked C:\Program Files (x86)\ to install NetBeans, which is pretty standard, but you know, Ruby is broken and can’t work in there, not even JRuby, so if you plan to use those at all, please consider installing it somewhere else.”

    I uninstalled NetBeans, or tried to. The uninstaller didn’t work. I deleted it and tried to install it on C:\ProgramFilesx86, which failed because some other directory created by NetBeans somewhere else existed from the previous installation, which halted the installation. I started a dance of run installer, remove dir, run installer, remove dir, run installer… until it worked.

    Once I finished I found out that NetBeans installed in C:\ProgramFilesx86\Netbeans 6.7.1. Yes, that’s a space. Oh my…

    As a bonus, NetBeans can’t automatically find Sun’s JDK in its default directory. I had to point to it by hand. Sun was, as usually, absolutely disrespectful of the platform conventions and installed its crap in C:\Sun. I would have picked another place but I thought “I’m sure some stupid program will want to pick that shit from there”. Silly me.

    12 hours have passed and I still haven’t been able to write a single line of source code. I contemplated installing Ruby by hand, but it’s so ugly that I decided I’m not going to use Windows for this. I’m going to work on another platform where installing Ruby is trivial and where I would probably never touch NetBeans because I have other editors.

    I know there’s a lot not really related to NetBeans here, for example, the fact that working with Python, or Ruby or MySQL in Windows is a pain; but it’s a great opportunity for NetBeans. There are developers wanting to use those languages and environments and if NetBeans makes it easy for them, they will pick NetBeans not because of its editor, but because of everything else (which is what I was hoping to get out of NetBeans).

    Aside from doing some usability tests, the people working on NetBeans should learn from the people working on Ubuntu (not the people working on Evolution) and instead of asking me for debugging traces when I report a simple obvious bug and then tell me it’s not their fault, they should submit those bugs upstream, to Ruby, gem, or whatever. Whenever someone like me submits that bug to NetBeans they should mark it as duplicate of an existing open bug that points to the upstream bug. I would have followed that link and told the Ruby developers “wake up!”. As it is, I didn’t. It’s too much work for me.

    Reviewed by Daniel Magliola. Thank you!