Wednesday, August 14, 2013

Learning Ruby, Rails and Git on OSX

Every so often folks ask me what they need to know to learn ruby. I have a variety of resources I send them and thought it might be helpful to put those all into one blogpost that I can point to in the future. If anyone has any additional thoughts please let me know in the comments. Thanks!

There are a wide range of tutorials / books and online resources for learning the "typical" Ruby on Rails stack. Listed below are a number of educational resources as well as common tools used by members of the ruby community. 

Learning Ruby

Online Tutorials
     http://rubykoans.com/
          - Built by NEO Columbus, a technology consulting company in Columbus, OH, the Ruby Koans take an iterative "zen-like" approach to learning the      language. Each tutorial takes the form of a step on the path to "enlightenment" while hitting the major aspects of the Ruby language. Despite the gimmicky setup this is one of the best tutorials out there. 

     http://tryruby.org/levels/1/challenges/0 
          - Produced by codeschool.com try ruby is a walk through of the language borrowing from "_Why's Poignant Guide to Ruby." _Why's book is a bit… odd.. but this tutorial still hits on the major points of the language. 

     http://www.ruby-lang.org/en/documentation/quickstart/
          - ruby-lang is the official site of the language. This is their quickstart guide, It's ok but a bit simple, doesn't hit a lot of the most useful libraries. 

Books
     http://pragprog.com/book/ruby4/programming-ruby-1-9-2-0
          - Referred to as "The Pickaxe Book", Programming Ruby is generally regarded as the authoritative print resource for the language.

     http://shop.oreilly.com/product/9780596516178.do
          - O'Reilly's version of a reference book. This may not have been updated for Ruby 2.0 yet.


Learning Rails   

Online Tutorials
     http://railsforzombies.org/
          - Also produced by codeschool.com this is a great primer on the basics of Rails version of MVC and core rails.
     
     http://ruby.railstutorial.org/
          - I've not actually run through this tutorial, but it comes recommended from DHH - the original creator of the rails framework.

     http://guides.rubyonrails.org/
          - Not really a tutorial but the documentation on the official site is some of the best language docs I've ever read. The guide are a primer on the major aspects of the framework and are worth reading through. 

Books
     http://pragprog.com/book/rails4/agile-web-development-with-rails-4
          - Keeping with the theme of The Pragmatic Bookshelf publishing the definitive references in the Ruby world, Agile Web Development with Rails is the most commonly recommended book on Ruby on Rails. 

Learning Git
Most Rails projects use Git as their source control system. Specifically most projects host their source control on github.com. Here are a few resources for someone either learning SCM with Git for the first time or transitioning from another SCM solution. 

Online Tutorials
     http://gitimmersion.com/
          - Another online tutorial by NEO, Git Immersion walks through the most important features of Git including places where it differs from traditional SCM. 

     http://try.github.io/
          - Also from Code School, this interactive online tutorial covers the basics of Git.

     http://www.youtube.com/GitHubGuides   
          - I haven't walked through these videos, but they come from Github lending a bit of credibility.

Books

     http://pragprog.com/book/tsgit/pragmatic-version-control-using-git
          - Prag Prog's take on their "Pragmatic Version Control" series. If you are used to the previous books in the series on CVS and SVN then this should be familiar. 


Testing
The Ruby world tends to be pretty strongly behind the notion of unit testing as a means for building, and debugging your application. Further a very large contingent of the community is focused on a sub-set of unit testing called BDD or Behavior Driven Development. The main difference between BDD and traditional testing is intent. BDD is focused on writing tests using a language that is oriented at the business requirements of an application and not the technical requirements. In Ruby a number of tools have emerged to make this a bit easier. The following tools are commonly used in Rails projects but are not part of the core Rails stack and will not be covered by the tutorials listed above.

RSpec - The most commonly used BDD tool, most rails projects substitute the baked in Test::Unit with RSpec
     Github Repo: https://github.com/rspec/rspec
     Online Tutorial: http://www.codeschool.com/courses/testing-with-rspec
     Book: http://pragprog.com/book/achbd/the-rspec-book

Cucumber - Sometimes Cucumber will be referred to as Acceptance Test Driven Development. The main distinction is that Cucumber tests are usually full stack including browser interaction, where RSpec tests are at a unit level. 
     Github Repohttps://github.com/cucumber/cucumber
     Bookhttp://pragprog.com/book/hwcuc/the-cucumber-book
     Related Tools: Capybara (https://github.com/jnicklas/capybara), Watir-Webdriver (https://github.com/watir/watir-webdriver)  


Text Editors and Development Environments
There's a pretty wide range of environments people use to develop with Ruby. The most common is a very terminal-focused workflow involving a good understanding of Bash, ZSH or some other common unix shell. It's recommended that anyone learning by Ruby and a Unix-style environment for the first time spend some time familiarizing themselves with the basics of the bash shell and get comfortable working in the terminal. 

http://shop.oreilly.com/product/9780596009656.do - is a great book for learning Bash.

There's also an assortment of text editors people tend to work with ranging from in-console editors like VIM or Emacs, to full fledged IDEs like Rubymine. Here are some of the better options. 

VIMhttp://www.vim.org/ - Over 20 years old vim is still regarded as one of the most customizable programming editors out there. There are thousands of plugins to improve workflow and an infinite ways to modify this editor to fit your needs. The downside of VIM is that it employs a somewhat distinct "modal" approach to editing requiring a significant learning curve. VIM also puts a lot of emphasis on keyboard efficiency requiring the user to master some somewhat esoteric keyboard commands to accomplish common tasks. Platform-specific wrappers for VIM are available for any major OS.

EMACS http://www.gnu.org/software/emacs/ - Clocking it at around 35 years old emacs has also withstood the test of time.  Emacs is as customizable as VIM and also as universal, running on any major OS platform. One advantage of Emacs is that it is built on top of, and allows the execution of Lisp scripts. This makes the editor extremely customizable while also utilizing a "real" programming language unlike vim's proprietary vimscript. Also worth noting is that a number of default emacs shortcuts work throughout OSX. Platform-specific wrappers for Emacs are available for any major OS.

Sublime Text 2http://www.sublimetext.com/2 - Sublime text 2 has quickly become the most popular programmers editor in Ruby. It runs outside of the console and doesn't depend on a fairly complex and specific set of shortcuts and commands to use. As a result the learning curve is relatively low. It also supports the old Textmate bundle (plugin) architecture, allowing users to import any of the bundles written for the previous king of ruby editors - Textmate. 

RubyMinehttp://www.jetbrains.com/ruby/  - For those looking for a more traditional IDE, RubyMine is the best on the market. It offers integrated source management, test execution, refactoring support, auto-completion and other common IDE features. One downside - it has (or at least had when I last used it) a tendency to run a bit slowly. That said Jetbrains makes great development tools and RubyMine is always improving. 

Ruby Development on OSX
I'm most familiar with building Ruby / Rails applications so I'm going to include a quick run-through of the stages involved in installing an OSX development environment. I'm not actually setting up a machine right now and documenting the stagings so there may be a few mistakes here, I'll update the next time I have a chance to run through that process. 


  1. Install XCode and the Command Line Development Tools
    You can get XCode from the App Store or by visiting https://developer.apple.com/xcode/ and clicking the link to the app store.

    Once installed you want to open XCode and visit the XCode -> Preferences -> Downloads screen, then click "Install" next to Command Line Tools.
  2. Install Homebrew - http://brew.sh/
    Homebrew is an excellent package management tool for console/daemon style apps in OSX. You will use it to install any number of needed servers (MySQL, Postgres, Redis, MongoDB, etc.
  3. Install RVM - https://rvm.io/
    The Ruby Version Manager, RVM allows you to have different versions of ruby on your machine at the same time. This is useful when working on multiple projects, it will also ensure that the pre-installed system ruby is left unmodified.

    Make sure you follow the instructions when installation completes to add RVM to your path. 
  4. Install Ruby 2.0 and make default.
    You may need a different version of ruby for a specific project but it's a good idea to install the latest version for general use.
    > rvm install ruby-2.0.0
    > rvm use ruby-2.0.0 --default
  5. Install bundler gem
    Bundler will be used to download every other important rubygem for your project. You need to install this upfront in order to be able to later download the rest of the necessary libraries
    > gem install bundler
  6. Brew install the necessary databases
    > brew install mysql
    > brew install postgres
    > brew install mongodb

    Make sure you follow the instructions after installation to add the databases to system startup
  7. Create an SSH Key for use with Github (and other services)
    > ssh-keygen
    Follow the on screen prompts, you can optionally enter a password or just press enter. This will create a folder - ~/.ssh/ with 2 files id_rsa and id_rsa.pub. id_rsa is your private key and you should not hand that out to anyone, treat it like a password. id_rsa.pub is your public key. You can upload that to your github account, under account settings -> SSH Keys
  8. Setup git global configuration options.
    > git config --global user.name "Your Name"
    > git config --global user.email "youremail@domain.com"
  9. git clone your project
    Create a folder for your projects, (I use ~/Projects) and clone the repository there. The command will look something like this.
    ~/Projects> git clone git@github.com:GHAccount/ProjectName.git
  10. cd into the directory and follow any instructions from RVM.
    ~/Projects> cd ProjectName

    If RVM is setup in the project you'll see a big wall of warning text, and possibly an error about not having the proper ruby version / gemset. To fix the ruby version problem just run rvm install <version>. Once that's done you can cd .. to go back to ~/Projects then cd ProjectName to try again.
At this point directions will vary based on your project. It would be best to consult your team for further setup instructions


Sunday, February 3, 2013

Specing an initializer block in a Rails Engine

Im currently in the process of writing a ruby gem that acts as a plugin CMS for an existing rails application. To do this I'm learning a lot about Rails Engines.

I may write more about my experiences with rails engines but for those who haven't used this (awesome) feature yet a rails engine is basically a gem (or plugin... for now) that not only interacts with a rails application, but can inject its own controllers, models, views and other standard rails components into your application.

As part of this CMS I want to dynamically map routes to the controller path in the gem that handles displaying a page. So if the user creates a page called Foo with a "slug" of foo/bar I want to dynamically bind a route like:

get "/foo/bar", :to => "cmsgem/pages#display"

This should happen when a page is published (if the route doesn't already exist.) It should also happen when the application spins up. It should look at all published pages and bind them.

To do this you can define an initializer in your main rails engine class. It looks something like:


module Cmsgem
  class Engine < ::Rails::Engine

    initializer 'cmsgem.bind_dynamic_routes', :after => :disable_dependency_loading do |app|
      app.routes.draw do
        Cmsgem::Page.where(:published => true).each do |p|
          get "/#{p.slug}", :to => "cmsgem/pages#display"
        end
      end
    end

  end
end

(note: if you want to know why i have the :after => :disable_dependency_loading check this article)

The problem I ran into was, how do I test this? This code executes when you initialize rails. Rspec spins up it's access to rails prior to running your tests, which means I can't create a Page and test for the existence of that route, because the page would be created after initialization and not get referenced when the code block above runs.

So I dug into the routing API a bit, and came up with this:


describe Cmsgem::Engine do
  it "dynamically adds routes from pages at initialization" do
    page = Cmsgem::Page.create(:slug => "rspec/test", :title => "Rspec Test", :content => "This is an rspec test")

    initializer = Cmsgem::Engine.initializers.select { |i| i.name == "cmsgem.bind_dynamic_routes" }.first
    initializer.run(Rails.application)

    route = Rails.application.routes.recognize_path("/rspec/test", :method => :get)
    route.should_not be_nil
  end
end

Basically what's happening is that I'm finding my custom initializer in the collection of initializers that my gem runs, then explicitly rerunning it. You have to pass in the rails application as it is used to bind the routes in the gem. 


I'd be curious if there's a better solution to this problem. Maybe some way to setup a record in rspec pre-initializer or a better way to reinitialize rails. If anyone has any suggestions let me know. Hopefully I'll post more about this gem as I go along.