Skip to content
This repository has been archived by the owner on May 16, 2019. It is now read-only.

fs/rails3-saas

Repository files navigation

Skeleton for new Rails 3 based application

This simple application includes ruby/rails technology which we use in the Flatsoft for new projects. Application currently based on Rails 3 stable branch.

Gems

Initializers

  • config.rb - loads configuration for current Rails environment to the configatron, so you can access them like configatron.app_name

  • devise.rb - setup devise options, mailer_sender and pepper from config

  • formtastic.rb - setup formtastic options

  • tabletastic.rb - setup tabletastic options, by default all actions rendered in the list

  • mailer.rb - setup default hosts for mailer from configuration

  • simple_navigation.rb - setup simplae navigation options

  • time_formats.rb - setup default time formats, so you can use them like object.create_at.to_s(:us_time)

  • requires.rb - automatically requires everything in lib/ & lib/extensions

Quick start

  • clone repository

  • tune gemspec name in the .rvmrc

  • tune config/config.yml

  • tune in the sources application name: s/Rails3Saas/YouApplicationName/g

  • tune config/database.yml.example and copy it to the config/database.yml

  • bundle install

  • rake cucumber

  • for autotest run in the shell “export AUTOFEATURE=true” and than run autotest command

Scaffolding

Scaffold generator will create: model with rspec, factory, controller based on inherited resources, views based on formtastic & tabletastic.

$ rails g scaffold post title:string text:text

Cucumber:feature generator will create: cucumber feature for scaffold resource

$ rails g cucumber:feature post title:string text:text

Tests

We use rspec with shoulda matchers for model testing and cucumber with capybara for integration testing.

Rspec

You should cover validations, associations with shoulda matchers and test deeply complected model methods. Check out for example user_spec.rb

describe User do
  it { should allow_mass_assignment_of(:full_name) }
  it { should allow_mass_assignment_of(:email) }
  it { should allow_mass_assignment_of(:password) }
  it { should allow_mass_assignment_of(:password_confirmation) }

  it { should validate_presence_of :full_name }
end

Use shortcuts specify {}, it {} and subject {}

subject { @user.address }
it { should be_valid }

Start context with ‘when’/‘with’ and methods description with ‘#’

Use RSpec matchers to get meaningful messages

specify { user.should be_valid }

Only one expectation per it block

describe DemoMan do
  before(:all) do
    @demo_man = DemoMan.new
  end

  subject { @demo_man }

  it { should respond_to :name   }
  it { should respond_to :gender }
  it { should respond_to :age    }
end

(Over)use describe and context

describe User do
  before { @user = User.new }

  subject { @user }

  context "when name empty" do
    it { should not be_valid }
    specify { @user.save.should == false }
  end

  context "when name not empty" do
    before { @user.name = 'Sam' }

    it { should be_valid }
    specify { @user.save.should == true }
  end

  describe :present do
    subject { @user.present }

    context "when user is a W" do
      before { @user.gender = 'W' }

      it { should be_a Flower }
    end

    context "when user is a M" do
      before { @user.gender = 'M' }

      it { should be_an IMac }
    end
  end
end

Test Valid, Edge and Invalid cases

describe "#month_in_english(month_id)" do
  context "when valid" do
    it "should return 'January' for 1" # lower boundary
    it "should return 'March' for 3"
    it "should return 'December' for 12" # upper boundary
  context "when invalid" do
    it "should return nil for 0"
    it "should return nil for 13"
  end
end

eggsonbread.com/2010/03/28/my-rspec-best-practices-and-tips/

Cucumber features

Organization

Group steps by model. We’ve found the best way to keep track of them is to group them by the primary model they affect. Some steps may affect multiple models, but usually there is an obvious choice.

Put each feature in it’s own file. Don’t be afraid to put features in subdirectories. For any large app, it’s almost essential.

Keep the file organized grouping the steps by Given / When / Then.

Do not overload the files generated by Cucumber like step_definitions/web_steps.rb and support/env.rb with your own steps, helpers or setup code. These files are likely to get overwritten when you update Cucumber so store your stuff in your own files.

Custom steps make your scenario DRY and accessible

Scenarios should have the same lifecyle as your code: Red, Green, Refactor to make them DRY and easy to read.

Group multiple steps together. For instance:

When I fill in "Title" with "New title"
And I fill in "Text" with "New text"
And I press "Submit"

could be refactored to:

When I submit valid post details

Background: setup the DRY way

Make the feature focus on one business object/action/context and the background will get longer than the scenarios.

Use ‘Background’ to consolidate common steps in a feature:

Background:
  Given I am an authenticated user

Factory Girl steps

Factory girl comes with some really useful cucumber steps. Try to use Factories as match as possible. For example if you have Post with different states like confirmed and unconfirmed it’s good practice create two factories for this cases:

Factory.define :post do |f|
  f.title 'How to write a good post'
  f.text 'Lorem ipsum?'
  f.confirmed true
end

Factory.define :unconfirmed_post, :parent => :post do |f|
  f.confirmed false
end

And then use them in the steps:

Given a post exists
Given a unconfirmed post exists

Checkout also:

Formtastic

If you have any difficulties with fortastic fill free to use standard form helpers. But better to extend fortastic with custom field types.

# lib/formtastic/money.rb
module Formtastic
  module Money
    protected

    def money_input(method, options = {})
      html_options = { :size => 6 }.merge(options.delete(:input_html) || {})
      html_options = default_string_options(method, :string).merge(html_options)

      self.label(method, options_for_label(options)) <<
        template.content_tag(:span, '$', :class => 'char') <<
        self.text_field(method, html_options)
    end
  end
end

Formtastic::SemanticFormBuilder.send(:include, Formtastic::Money)

# config/initialisers/formtastic.rb
require 'formtastic/money'

# in the view
<%= form.input :price, :as => :money %>

Simple Navigation

Navigation configuration stored in the config/navigations folder. We have main and user navigation which accessible for visitor and authorized user accordingly.

Note on Patches/Pull Requests

  • Fork the project.

  • Make your feature addition or bug fix.

  • Add tests for it. This is important so I don’t break it in a future version unintentionally.

  • Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)

  • Send a pull request. Bonus points for topic branches.

How to update existing project with new changes from rails3-base repo

git remote add rails3-base git://github.com/fs/rails3-base.git
git checkout -b rails3-base-update
git pull rails3-base master
# fix conflicts
# commit
# test
# merge

Thanks, Flatsoft

About

Skeleton for SAAS application based on rails 3

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published