Skip to content

Instantly share code, notes, and snippets.

@mauricioszabo
Created March 27, 2012 21:57
Show Gist options
  • Save mauricioszabo/2220745 to your computer and use it in GitHub Desktop.
Save mauricioszabo/2220745 to your computer and use it in GitHub Desktop.
Coding Guides

CODING GUIDES

Well, there are a bunch of people making these kind of "guides", so I think it's time to make mine too...

In this guide, must and should are used to define mandatory or recomended, in that order.

Naming:

  • variable's and method's names must be in snake_case.
  • classes' and modules' names must be in CamelCase.
  • objects should follow classes' names, like @person = Person.new, or empty_person = Person.new
  • any method that changes object's state should be named with an exclamation point: method_name!. It should be good too if these methods don't return a meaning result (ever had problems with "string".upcase! returning nil?)
  • any method that returns a boolean should be named like method?
  • any method that is named with an question mark (like method?) must return a boolean (true or false. Don't make Ruby's dynamic type more surprising than already is).
  • if the return of a method? is not a boolean but is an information related to the "question" of the method, it should be returned. For example: "I am Wally".is_there_wally? => 5. But it must be meaningful.

Identation

  • multiple identations should be avoided: separate your code on methods
    def create_magazines
      magazines = ThirdPartyBookstore.all
      magazines.each { |m| create_magazine m }
    end

    def create_magazine(data)
      magazine = Magazine.new data
      magazine.save!
    end
  • "if" and "unless" as modifiers must be used everytime that is possible
    def save
      return unless valid?
    end
  • every when on case must never have more than one command:
    message = case attribute
      when String then "It's a String!"
      when Numeric then "It's a Number!"
      else "Don't know what this is..."
    end

    \#or
    class ObjectFactory
      def make(from_what)
        case from_what
          when :admin then AdminUpdater.new
          when :user then instantiate_user_updater
        end
      end
    end

Source-code Organization

  • methods that are called from other methods must be below their callers
  • these "dependent methods" must be in order which they are called
    def a_method
      calling_one
      calling_two
      calling_three
    end

    def calling_one
      calling_three
    end

    def calling_two
      puts "Woo"
    end

    def calling_three
      puts "Three"
    end
  • methods must have, at most, 30 lines (so they appear on only one screen)

  • to permit the above requirements, private should be used as a keyword, and must be used directly below the method:

    def public_method
      do_something
    end

    def do_something
    end
    private :do_something

Tests

I don't know who beginned with the idea of "describe Array#each" or such but...

  • don't reference methods' names on description of specs. This ties internal structure to tests
  • describe what that test is, in fact, testing: it "creates a new user" do
  • context and describe are for grouping similar tests. Avoid using then too much, for example:
    describe UsersController do
      context "on POST" do
        context "create" do
          context "when giving valid data" do
            it "..."
  • don't test internal structure. Ever. Yes, rspec-rails' assigns(:instance_variable), I'm talking to you...

  • make sure you're testing something. So, avoid things like:

    it "not raises any error if user is valid" do
      user = User.new :name => "foo"
      user.save! #What are you testing here?
    end

    it "has many slaves" do
      User.should have_many(:slaves) #What do you need this for?
    end

    it "is unique" do
      User.stub!(:where).with(:name => "Foo").and_return([mock(User, :name => "Foo")])
      user = Factory.stub :user, :name => "Foo"
      user.should_not be_valid #Do you guarantee that this asserts uniqueness?
    end
  • don't use "implicit" values on your tests. What happens if one attribute cease to be mandatory, for example, or needs to change its default value on a factory?
    it "concatenates name an surname" do
      #WRONG WAY:
      user = Factory :user
      user.complete_name = "Foo Bar" #DON'T!
      #CORRECT WAY:
      user = Factory :user, :name => "Foo", :surname => "Bar"
      user.complete_name = "Foo Bar"
    end
  • try to mantain your tests small. If, for some reason, you create a helper method that automates some task, create a custom matcher for it too. For example, supose you create a helper that shows error message boxes. So don't do this:
    visit something_that_will_raise_an_error_path
    within "div#errorMessageBox" do
      within "div.errorTitle" do
        page.should have_content("Critical Error!")
      end
      within "div.errorMessage" do
        page.should have_content("No space on device!")
      end
    end

Instead, do this:

    visit something_that_will_raise_an_error_path
    page.should have_a_message_box(:title => "Critical Error!", :body => "No space on device!")
    \#or
    page.should have_a_message_box("Critical Error!").with_text("No space on device!")

Other Guides

  • exception handling must be made on a specific method
    def update_file
      file = open_file
      return if file.nil?
      file.puts "Updated!"
    end

    def open_file
      File.open("/tmp/something", "w")
    rescue
      return nil
    end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment