Search code examples
rubymacoscommand-linerubymine

From RubyMine to Mac command-line app?


I'm a fairly accomplished developer, and know enough Ruby to shoot my foot from several polymorphic directions. Honestly, I have no interest in Rails and want to work with Ruby strictly as a Mac command line scripting alternative to php or Python. I'm stumped on where to even start with RubyMine. I'd like to use RubyMine for all of its features, and I use JetBrains' AppCode and IntelliJ so RubyMine seems like a no-brainer. But am I limited to writing .rb files and simply running them from the Terminal command line? I'd like to be able to debug code without waiting for an exception.

So, I guess what I'm asking for is a simple checklist to get to a command-line app. Which project type, how should I organize it, how to test, and how to run from inside RubyMine? Thanks in advance. (We were all newbies once...)


Solution

  • TLDR; Just code away, use RubyMine when you can, and you'll be fine. I include my own process here, but just as an example.

    Creating Command Line Tools in Ruby

    I use RubyMine without doing any rails and doing just what you ask. I write a lot of simple scripts, and large applications, both often usable from a command line.

    I still organize my code into a 'project directory'. And point RM at that directory. Instead of just working entirely in a file, and trying to point RM at the file alone. This seems to be what IDE's prefer. This lets RM create its .idea directory to hold all of its project related settings.

    This may mean that some projects consist of nothing more than a directory (for the project) and a single file in that directory (the single script which is really the only content in that project).

    Benefits of a Project Directory

    You'll soon find you add more files to any project anyways. Both additional coding and various utilities which are part of the great benefit of ruby.

    • gem
    • rake
    • tests

    The project directory pattern, also lets you version the code with popular VCS such as git, and even upload to social coding sites like github.

    Project Setup

    I don't bother with any sort of "project type". In fact I don't create projects from with RubyMine at all. I just create a directory, and then open that directory with RM (whether from the File Menu, or via the "mine" command line tool that RM installs).

    project/

    Of course project isn't the real name of the project, or the directory I create, I'm just using it as a generic name here. I would actually name the directory (and project) after the scripts or subject matter of the work they do.

    NOTE: currently there is a bug in the mine command in RM 5 where it won't open a second project window.

    Obviously if this is a project that includes any command line scripts I'll put those in a bin directory, just for organization purposes. I don't like to clutter the root project directory with all sorts of things. There are too many tools that require their settings files in the project root, so the fewer the better.

    project/bin/

    Like I said, various other utilities prefer their settings files in the project root, so you may find yourself using some of them.

    project/Gemfile
    project/Rakefile
    project/project.gemspec
    project/README.md
    project/.rdoc_options
    ...
    

    As soon as I have some automated testing for the scripts (which I admit isn't immediate) I put all test code under a test directory, but I further categorize those according to the scope of the tests or how often I would run them during development. And I'll go so far as to add quck Rake tasks that are just aliases for the commands necessary to run the tests. So I dont' have to remember which test runner, or type the full paths each time.

    project/test/
    project/test/unit/
    project/test/unit/some_component_test.rb
    project/test/integration/
    

    Finally, as the project grows, and I add more scripts or want to reuse some of the code within external projects, I'll add a lib directory, and use some standard practices for how to organize that.

    project/lib/
    project/lib/project.rb
    

    And as the project continues to grow and get more complicated, more organization of the content is required, after a little refactoring. Note the cmd directory to contain the actual top-level code of various workflows a particular script can go through.

    project/lib/project/
    project/lib/project/other_stuff.rb
    project/lib/project/cmd/
    project/lib/project/cmd/one_command.rb
    project/lib/project/cmd/another_command.rb
    

    Of course the original project.rb 'requires' all of these lib files. Ruby loads pretty fast, so I don't bother too much with managing dependencies between the ruby files within the same project. Its really all about code readability.

    Better Command Line Apps

    There is nothing wrong with starting with a single script file and writing minimal boiler plate coding. And thats probably the best way to start.

    #!/usr/bin/env ruby
    
    =begin
    
    this is a script blah which does whatever
    
    EXAMPLE
    
    $ script <options> <arguments
    
    =end
    
    def main(args)
      whatever
    end
    
    main(ARGV)
    exit(0)
    

    But writing even better command line apps in ruby is pretty easy. There are popular gems which handle a lot of the infrastructure for you.

    You can start with something as simple as OptionParser

    Then as you build more code in your scripts you can graduate to a bit more infrastructure with a gem like Methadone

    Finally, when you're writing full blown command line applications, with many "internal" commands like "git submodule init ", you can ascend to something much more heavy-weight like GLI

    Testing Command Line Apps

    Unit testing is the pretty much the same, no matter what type of application your writing. You're not using real services or combining components, but just isolating the internal components and using them in a purely internal manner. So not much changes there for command line tools.

    But when it comes to integration or full command-line testing of my scripts, I'm actually a big fan of cucumber/aruba. I'm not big on TDD, I'm a bit too pragmatic for that. But for command line tools, the series of use cases is already laid out pretty plainly in the set of options available. Even for interactive commands this makes a series of feature files conveniently double as documentation for the tool.

    Feature:
      Create and manipulate notes in Evernote.
    
      Background:
        Given I am logged in as me
        And I have 0 notes
    
      Scenario: Show note
        Given that I have 1 note named "foo" with content "bar"
        When I run `rnote show note --title "foo"`
        Then the output should contain "bar"
    
      Scenario: Show note, from multiple notes
        Given that I have 2 notes named "foo"
        When I run `rnote show note --title "foo"` interactively
        And I type "1"
        Then the output should contain "foo"
    

    Installation

    As for installation and packaging for your scripts, so that they can be shared and reused by the rest of the world, look no further than the same 'gem' utility that you've already been using all along in ruby for depenencies.

    The gemspec you create when writing a new gem to share, can be told about your scripts and other command line utilities. It will then wrap them up with the rest of your code, install them in a good location, and even fix up the PATH so the user can find them when the time comes.

    In fact this works so well, your gem can be composed of nothing but these bin scripts. With no other re-usable code included.

    cat-dog is a harmless little gem I whipped up as an example for something else. It contains no lib directory at all and no other ruby other than a single command line script.

    Ruby Without Rails

    I've run into a lot of Rails developers who aren't cognisant of what is Rails and what is ruby. i.e. what you're left with when you remove the Rails. Obviously the server and templating, databasing, MVC are all gone, as are a portion of convenience methods added to every-day objects.

    The goods new is that you're still left with quite a lot. And if you find you miss something, its easy to add it back in with by including a gem or two. For instance, if you enjoy using an ORM like active-record it can be included back into your application, without rails, with little fuss.

    require 'activerecord'
    

    Full Featured RubyMine

    Finally, using RubyMine features more heavily during development. Everything available when working with rails is available for command line applications.

    You can run your commands from RM and use its debugger just the same as rails. Just like any IDE you can set up "run" profiles with the arguments you want to test the script on, and run or debug the script through the IDE. Personally, I never use a command-line debugger. If I need to fire up a debugger to troubleshoot some issue, then I'll launch the program in an IDE. The benefits of an IDE debugger with "heads-up" display of all the pertinent details, and visually following the script through execution, is just irreplaceable.