This simple application includes ruby/rails technology which we use in the Flatsoft for new projects. Application currently based on Rails 3 stable branch.
-
Basic auth: Devise github.com/plataformatec/devise, app/models/user.rb
-
OAuth auth: Omniauth github.com/intridea/omniauth, app/models/user/omni_auth_extension.rb
-
Navigation: SimpleNavigation github.com/andi/simple-navigation, config/navigations
-
DRY controllers: Inherited Resources, github.com/josevalim/inherited_resources
-
Form builder: Formtastic github.com/justinfrench/formtastic, config/initializers/formtastic.rb
-
List builder: Tabletastic github.com/jgdavey/tabletastic, config/initializers/tabletastic.rb
-
Default styles for Formtastic: Flutie, github.com/thoughtbot/flutie
-
Application config: Configatron, github.com/markbates/configatron, config/config.yml
-
JS Framework: Jquery-rails, github.com/indirect/jquery-rails
-
Tests: RSpec, Shoulda, RR, Cucumber, Factory Girl, Autotest
-
Code metrics: rails_best_practices, github.com/flyerhzm/rails_best_practices and rcov
-
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
-
clone repository
-
tune gemspec name in the .rvmrc
-
tune config/config.yml
-
tune in the sources application name: s/Rails3Base/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
We use Devise for login based auth b/c is modular and have a good community support. Take a look at config/initializers/devise.rb if you want customize it.
We use Omniauth for authientification over OAuth with Twitter. Please configure key & secret token in the config/config.yml If you don’t want to use omniauth simply comment it’s configuration in the config/initializers/devise.rb
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
We use rspec with shoulda matchers for model testing and cucumber with capybara for integration testing.
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/
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.
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
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 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
You can use @webmock for enabling webmock functionality around some scenario. It’s mean all real HTTP connection will be disabled in the given scenario and you need to stub them with stub_request. Checkout twitter_authentication_steps.rb for example.
You can use @vcr for enabling VCR functionality around some scenario. It will also disable all real HTTP connectons and will try to replay them from the cassette assotiated with this scenario. Each cassette will be stored in the features/cassettes/:feature_name/:scenario_name.
Checkout also:
-
collectiveidea.com/blog/archives/2010/09/09/practical-cucumber-factory-girl-steps/
-
collectiveidea.com/blog/archives/2010/09/13/practical-cucumber-organization/
-
eggsonbread.com/2010/09/06/my-cucumber-best-practices-and-tips/
-
github.com/aslakhellesoy/cucumber/wiki/Tutorials-and-Related-Blog-Posts
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 %>
Navigation configuration stored in the config/navigations folder. We have main and user navigation which accessible for visitor and authorized user accordingly.
-
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.
-
Also you can send bug reports in the PivotalProject www.pivotaltracker.com/projects/140789
git remote add rails3-base git:https://github.com/fs/rails3-base.git git checkout -b rails3-base-update git pull rails3-base master # fix conflicts # commit # test # merge
Thanks, Flatsoft