RubyMine Issues

RubyMine is my preferred IDE for Ruby development. I chose it for local and remote debugging from the browser. It has some space for improvement but it’s also pretty good. And support is excellent.

These are the issues that I’ve reported. This list is mostly for my own use.

  1. Replace in Path should be integrated into Find in Path
  2. The ‘Replace in Path’ functionality is completely broken
  3. Number of ‘Found usages’ depends on wether the button ‘Merge usages from the same line’ is pressed or not
  4. Exclude external libraries from a custom scope of ‘Find in Path’
  5. In a big text file, it takes up to 2 seconds to move the caret only 1 char
  6. Depending on path tooltip position, displayed document is garbled
  7. Depending on caret position, displayed document is garbled
  8. Replace in Path (with a grouping RegEx) makes a mess (depending on input)
  9. File comparison should behave differently for ending lines
  10. Breakpoints dialog floats on top even if the parent project is closed (it has no closing button either)
  11. Extract CSS is broken
  12. Dream remote debugging with the PassengerDebugger option
  13. How to remotely debug Rails in Apache + Passenger + RubyMine

Orderly require scripts from a bookmarklet

Abstracting a bit my bookmarklet for injecting jQuery, I’ve come to this one where you can orderly require needed scripts before executing a given payload.

javascript:(function () {

    var debug = !false;
    var requested = false;

    orderly(
        require_jQuery('1.8.3', 'j'),
        require_SimpleModal('j'),
        function() {
            payload(window.j);
        }
    );

    function payload(jQuery) {
        jQuery(function($) {
            $('body').click(function() {
                $('span:first').modal({
                    overlayClose: true,
                    opacity: 80,
                    overlayCss: {backgroundColor: "#000"},
                    maxHeight: 480,
                    maxWidth: 640,
                    containerCss:{
                        backgroundColor:"#fff",
                        borderColor:"#fff",
                        padding:20
                    }
                });
            });
        });
    }

    function require_jQuery(version, symbol) {
        return {
            url: 'http://ajax.googleapis.com/ajax/libs/jquery/' + version + '/jquery.min.js',
            is_loaded: function() {
                return !requested && typeof window[symbol] != 'undefined' && window[symbol].fn.jquery == version
                    ||  requested && typeof window.jQuery  != 'undefined' && window.jQuery.fn.jquery  == version;
            },
            before: function() {
                window.oldJQuery = window.jQuery;
                window.jQuery = window.undefined;
            },
            success: function() {
                jQuery.noConflict();
                window[symbol] = jQuery;
                console.info('jQuery ' + jQuery.fn.jquery + ' has been injected. (as "' + symbol + '")');
            },
            ensure: function () {
                window.jQuery = window.oldJQuery;
            }
        };
    }

    function require_SimpleModal(jQuerySymbol) {
        return {
            url: 'http://cdn.jsdelivr.net/simplemodal/1.4.2/jquery.simplemodal.1.4.2.min.js',
            is_loaded: function() {
                return !requested && typeof window[jQuerySymbol].modal != 'undefined'
                    ||  requested && typeof window.jQuery.modal        != 'undefined';
            },
            before: function() {
                window.oldJQuery = window.jQuery;
                window.jQuery = window[jQuerySymbol];
            },
            ensure: function () {
                window.jQuery = window.oldJQuery;
            }
        };
    }

    function orderly(i) {
        var stuff = Array.prototype.slice.call(arguments);
        if (typeof i === 'number') {
            stuff.shift();
        }
        else {
            i = 0;
        }
        if (i == stuff.length) return;

        console_log('orderly ' + i);
        switch (typeof stuff[i]) {
            case 'string':
                break;
            case 'function':
                stuff[i]();
                orderly.apply(null, [i+1].concat(stuff));
                break;
            case 'object':
                load_script(stuff, i);
                break;
            default:
                throw 'Expected a valid argument (' + i + ')';
                break;
        }
    }

    function load_script(stuff, i) {
        var current = stuff[i];
        var url = current.url;
        console_log('load_script: request for ' + url);

        requested = false;
        var is_loaded = current.is_loaded;
        if (is_loaded()) {
            console_log('load_script: already available');
            orderly.apply(null, [i+1].concat(stuff));
            return;
        }
        var before = current.before;
        var success = function() {
            call_function(current.success);
            call_function(current.ensure);
            orderly.apply(null, [i+1].concat(stuff));
        };
        var failure = function() {
            call_function(current.failure);
            call_function(current.ensure);
        };
        call_function(before);

        console_log('load_script: requesting');
        var s = document.createElement('script');
        s.setAttribute('src', url);
        document.getElementsByTagName('head')[0].appendChild(s);

        requested = true;
        var time = 0;
        var id = setInterval(function () {
            console_log('load_script: retry #' + time);
            if (is_loaded()) {
                clearInterval(id);
                console_log('load_script: done');
                success();
                return;
            }
            ++time;
            if (time == 50) {
                clearInterval(id);
                console_log('load_script: giving up');
                failure();
                return;
            }
            console_log('load_script: waiting');
        }, 100);
        console_log('load_script: requested');
    }

    function call_function(f) {
        return typeof f == 'function' ? f() : null;
    }

    function console_log(msg) {
        if (debug) console.log(msg);
    }
})();

The require_* functions could be made available from a central repository, so that you can find, copy and paste what you need. But going a bit forward, one could also make orderly support URIs like ‘ord://symbol:module/version’ or ‘ord://symbol:host/module/version’ like ‘ord://j:jquery/1.8.3’ or ‘ord://j:jquery/modal/1.4.2’. They could be used like this:

orderly(
        'ord://j:jquery/1.8.3',
        'ord://j:jquery/modal/1.4.2',
        function() {
            payload(window.j);
        }
    );

and because they are strings, they would not interfere with objects and functions. Execution will then be in two steps: first, request the definition object from the repository and, finally, request the script as specified by the definition object. This will be very similar to what Bundler does for Ruby.