moneybird - the rails side

21
The Rails side! Edwin Vlieg - BlueTools Twente.rb - november 2008

Upload: edwin-vlieg

Post on 02-Jul-2015

1.240 views

Category:

Technology


1 download

DESCRIPTION

Short presentation of the Rails side of MoneyBird, given at the Twente.rb meeting on 26 november 2008.

TRANSCRIPT

Page 1: MoneyBird - The Rails Side

The Rails side!Edwin Vlieg - BlueTools

Twente.rb - november 2008

Page 2: MoneyBird - The Rails Side

Edwin Vlieg — MoneyBird 2008

MoneyBird

• Simpele facturatie voor kleine bedrijven

• Facturen aanmaken, versturen en beheren

• Simpel contactbeheer

• Binnenkort periodieke facturen

• Innovatieve Ajax interface voor factuur bewerken

2

Page 3: MoneyBird - The Rails Side

Edwin Vlieg — MoneyBird 2008

MoneyBird

• April 2008: eerste idee

• Juni 2008: prototypes van applicatie gereed (HTML mockups met JavaScript functionaliteit)

• September 2008: test versie gereed

• Oktober 2008: live!

3

Page 4: MoneyBird - The Rails Side

Edwin Vlieg — MoneyBird 2008

The Rails side!+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐+‐‐‐‐‐+‐‐‐‐‐‐‐+| Name                 | Lines |   LOC | Classes | Methods | M/C | LOC/M |+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐+‐‐‐‐‐+‐‐‐‐‐‐‐+| Controllers          |   977 |   779 |      10 |      89 |   8 |     6 || Helpers              |   300 |   194 |       0 |      30 |   0 |     4 || Models               |  1340 |   983 |      22 |     127 |   5 |     5 || Libraries            |   830 |   575 |       4 |      61 |  15 |     7 || Model specs          |  1045 |   826 |       0 |       3 |   0 |   273 || View specs           |   252 |   189 |       0 |       0 |   0 |     0 || Controller specs     |   678 |   560 |       1 |       6 |   6 |    91 || Helper specs         |   185 |   162 |       0 |       0 |   0 |     0 || Library specs        |    68 |    57 |       1 |       0 |   0 |     0 |+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐+‐‐‐‐‐+‐‐‐‐‐‐‐+| Total                |  5675 |  4325 |      38 |     316 |   8 |    11 |+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐+‐‐‐‐‐+‐‐‐‐‐‐‐+  Code LOC: 2531     Test LOC: 1794     Code to Test Ratio: 1:0.7

4

Page 5: MoneyBird - The Rails Side

Edwin Vlieg — MoneyBird 2008

The Rails side!

• Rails 2.2 (since this week!)

• PostgreSQL

• Capistrano

• Developed in TextMate

5

Page 6: MoneyBird - The Rails Side

Edwin Vlieg — MoneyBird 2008

Plugins

• acts_as_state_machine

• attachment_fu

• i18n_labels

• localized_dates

• subdomain_fu

• restful-authentication

6

Page 7: MoneyBird - The Rails Side

Edwin Vlieg — MoneyBird 2008

Rails 2.2: I18n

• Scoping

# Override translate method to consider translate_scope. def t(text, options = {}) scope = (translate_scope.dup << options.delete(:subscope)).compact super(text, {:scope => scope}.merge(options)) end

# The current translation scope, default to current controller name. def translate_scope @translate_scope || [template.base_path] end

7

Page 8: MoneyBird - The Rails Side

Edwin Vlieg — MoneyBird 2008

Rails 2.2: I18n

• Localized labels, based on ActiveRecord::human_attribute_name

<tr> <th><%= f.label :name %></label></th> <td> <%= f.text_field :name %> <%= error_message_on :contact, :name %> </td> </tr> <tr> <th><%= f.label :contact_name %></th> <td><%= f.text_field :contact_name %></td> </tr>

8

Page 9: MoneyBird - The Rails Side

Edwin Vlieg — MoneyBird 2008

Rails 2.2: I18n

• In locales definition:

nl: activerecord: attributes: contact: contact_name: Contactpersoon name: Naam of Bedrijfsnaam

9

Page 10: MoneyBird - The Rails Side

Edwin Vlieg — MoneyBird 2008

Testing• RSpec

• For testing models (business logic)

• Covers 141 examples

• Cucumber

• Plain text stories for integration testing

• Covers 535 steps

• Factory girl

• Webrat10

Page 11: MoneyBird - The Rails Side

Edwin Vlieg — MoneyBird 2008

Testing: CucumberFeature: invoice An user should be able to manage his invoices Scenario: Create a new invoice Given I'm logged in as edwin at bluetools When I follow "New invoice" And I fill in "Name or Companyname" with "Test company" And I fill in "Address" with "Hengelosestraat 534" And I fill in "invoice_zipcode" with "7500AG" And I fill in "invoice_city" with "Enschede" And I fill in "invoice_new_detail_attributes__description" with "Test description" And I fill in "invoice_new_detail_attributes__price" with "123" And I press "Save" Then an invoice with invoice id "2008-0001" should exist And I should see "2008-0001" And I should see "Test company" And I should see "Test description" Scenario: Create a new invoice with no invoice details Given I'm logged in as edwin at bluetools When I follow "New invoice" And I fill in "Name or Companyname" with "Test company" And I fill in "Address" with "Hengelosestraat 534" And I fill in "invoice_zipcode" with "7500AG" And I fill in "invoice_city" with "Enschede" And I press "Save" Then no invoice with name "Test company" should exist And I should see "Details should contain at least one row"

11

Page 12: MoneyBird - The Rails Side

Edwin Vlieg — MoneyBird 2008

Testing: Cucumber

• Only ~30 steps defined!

• Example resource definition and assertion:Given /^I've created (a|an) ([A-z].*) with ([A-z ].* ".*"( and )?)+$/ do |_, resource, fields, _| #' fields = fields.split(" and ").collect { |f| r = f.strip.match(/^([A-z ].*) "(.*)"$/); [r[1].tr(" ", "_").underscore, r[2]] }.flatten Factory(resource.tr(" ", "_").to_sym, Hash[*fields])end

# Then a company with name "foobar" should existThen /^(a|an|no) ([A-z].*) with ([A-z ].*) "(.*)" should exist$/ do |condition, resource, field, value| if resource.to_class.respond_to?(:exists?) condition = condition == 'no' ? false : true resource.to_class.exists?({ field.tr(" ", "_").underscore => value }).should == condition else raise "Resource #{resource.classify} isn't a model" endend

12

Page 13: MoneyBird - The Rails Side

Edwin Vlieg — MoneyBird 2008

PDF generation

• PDF writer:

• API.... :-(

• PNG file errors (‘PNG uses more than 8 bits’, ‘Invalid filter algorithm 61’)

13

Page 14: MoneyBird - The Rails Side

Edwin Vlieg — MoneyBird 2008

PDF generation

• Prawn

• API... :-)

• Better PNG support

• Only the Euro sign doesn’t show up

14

Page 15: MoneyBird - The Rails Side

Edwin Vlieg — MoneyBird 2008

PDF generationPrawn::Document.generate("fancy_table.pdf") do

data = [["Gregory Brown", "[email protected]" ], ["James Healy" , "[email protected]" ], ["Ross Perot" , "[email protected]" ], ["Al Gore" , "[email protected]" ], ["Ralph Nader" , "[email protected]" ]]

table data, :position => :center, :headers => ["Name", "Email"], :row_colors => ["ffffff","ffff00"], :vertical_padding => 5, :horizontal_padding => 3end

15

Page 16: MoneyBird - The Rails Side

Edwin Vlieg — MoneyBird 2008

Concerned_with

• Problem: Invoice model has 450 LOC

• Other models have on average 200 LOC

• Make the model less fat with concerned_with

16

Page 17: MoneyBird - The Rails Side

Edwin Vlieg — MoneyBird 2008

Concerned_with# In config/initializersclass << ActiveRecord::Base def concerned_with(*concerns) concerns.each do |concern| require_dependency "#{name.underscore}/#{concern}" end endend

# In models/invoice.rbclass Invoice < ActiveRecord::Base concerned_with :validations, :states, :policies, :scopes ...end

# In models/invoice/validations.rbclass Invoice validates_presence_of ...end

17

Page 18: MoneyBird - The Rails Side

Edwin Vlieg — MoneyBird 2008

Invoice edit interface

• Difference between new and edit action:

• ‘edit’ is performed on an persistant instance

• ‘new’ does not exist after the request has finished

18

Page 19: MoneyBird - The Rails Side

Edwin Vlieg — MoneyBird 2008

Invoice edit interface

• Solution: always load the complete form on the background

• Switch between display and form with JS

• Just plain old submitting the complete form for creating a new record

19

Page 20: MoneyBird - The Rails Side

Edwin Vlieg — MoneyBird 2008

Invoice edit interface

• Input is validated and rendered via an Ajax call.

<div id="invoice_description"> <div class="show"> <%= render :partial => "invoice_description" %> </div> <div class="form"> <%= render :partial => "invoice_description_form" %> </div> </div>

20