agile and rails

Post on 19-Jan-2015

78 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

 

TRANSCRIPT

Agile Rails

Gregory McIntyre, March 2012

My Creds

•University

•Ruby, TDD

•Tilefile Pty Ltd

•Python, ActionScript, Rails

•realestate.com.au

•Agile (ThoughtWorks)

Waterfall

#fail

•Impenetrable requirements documents

•Scary contracts

•Early is when you know the least

•Requirements tend to change

Agile Manifest’O

•Face time > processes and tools

•Working software > documentation

•Customer collaboration > contracts

•Embracing change > sticking to the plan

You Say Scrum, I Say...

•XP

•Scrum

•Crystal

•Lean

•Roll your own, just follow the ’festo

Turn, Turn, Turn

•Strategy

•Release

•Iteration

•Daily

Strategy

Strategy

•Mission statements

•Scope

•Risk

•Team building

•Priorities

•Finance

Project Inception

•Agile “strategy” process (1-5 days)

•Share vision

•Align goals

•Set realistic expectations

•Swap phone numbers

Strategy Constraints

Risk Chart

Release Planning

Build a Story Backlog

•Do some UX

•Write “stories”

•T-shirt sizing

Burn Down Chart

Kanban• Toyota invented it to

manage car production efficiently

•Not waterfall, not iterative

•Continuous, like a pipe

•Deliver something of value every day

Lumpy Bad

Smooth Good

Iterations

Feedback Loops

• 2-4 weeks is common

• Involve the customer

•Revise estimates

• Assess and improve

Planning Poker

Card Wall

Stand Ups

Pair Programming

Conversations

Noise

Mess

Paper and Pens

Test Driven Dev

Behaviour Driven Dev

Continuous Integration

Agile is...

•Technically “backward” (ahem, pragmatic)

•“Last minute”

•Noisy, demanding and confronting

•Practice practice practice

•About visibility, not due dates

Agile is...Deliver something of value every day

Ruby on Rails

Ruby: Expressive and Flexible

exit unless "restaurant".include? "aura"

5.times { print "Odelay!" }

['toast', 'cheese', 'wine'].each {|food| print food.capitalize }

class Blog has_many :postsend

Ruby DSLs

class Blog has_many :postsend

def has_many(things)

“LADIES, let’s make web apps with Ruby”

Rails is a Web Application Framework

•Handles an HTTP request

•Common practices

• Sensible defaults

• Keeps things orderly

Rapid Prototyping$ rails generate scaffold Post name:string title:string content:text invoke active_record create db/migrate/20120316050430_create_posts.rb create app/models/post.rb invoke rspec create spec/models/post_spec.rb route resources :posts invoke inherited_resources_controller create app/controllers/posts_controller.rb invoke erb create app/views/posts create app/views/posts/index.html.erb create app/views/posts/edit.html.erb create app/views/posts/show.html.erb create app/views/posts/new.html.erb create app/views/posts/_form.html.erb invoke rspec create spec/controllers/posts_controller_spec.rb create spec/views/posts/edit.html.erb_spec.rb create spec/views/posts/index.html.erb_spec.rb create spec/views/posts/new.html.erb_spec.rb create spec/views/posts/show.html.erb_spec.rb invoke helper create spec/helpers/posts_helper_spec.rb create spec/routing/posts_routing_spec.rb invoke rspec create spec/requests/posts_spec.rb invoke helper create app/helpers/posts_helper.rb invoke rspec invoke stylesheets create public/stylesheets/scaffold.css

Sensible DefaultsDavidson::Application.routes.draw do resource 'shared_cookie'end

shared_cookie POST /shared_cookie new_shared_cookie GET /shared_cookie/newedit_shared_cookie GET /shared_cookie/edit GET /shared_cookie PUT /shared_cookie DELETE /shared_cookie

<struts>

<package name = "MyPackage">

<interceptors> <!--Some set of common interceptors for a particular action--> <interceptor name = "A_I1" class = "MyA_I1"> <interceptor name = "A_I2" class = "MyA_I2"> <interceptor name = "A_I3" class = "MyA_I3"> <interceptor name = "A_I4" class = "MyA_I4">

<!--Another set of common interceptors --> <interceptor name = "B_I1" class = "MyB_I1"> <interceptor name = "B_I2" class = "MyB_I2"> <interceptor name = "B_I3" class = "MyB_I3"> <interceptor name = "B_I4" class = "MyB_I4"> </interceptors>

<interceptor-stack name = "A"> <interceptor-ref name = "A_I1"> <interceptor-ref name = "A_I2"> <interceptor-ref name = "A_I3"> <interceptor-ref name = "A_I4"> </interceptor-stack> <interceptor-stack name = "B"> <interceptor-ref name = "B_I1"> <interceptor-ref name = "B_I2"> <interceptor-ref name = "B_I3"> <interceptor-ref name = "B_I4"> </interceptor-stack>

<action name = "MyAction1"> <interceptor-ref name = "A"/> </action>

<action name = "MyAction2"> <interceptor-ref name = "B"/> </action>

</package>

</struts>

ERB versus HAML<%= form_for(@post) do |f| %>  <% if @post.errors.any? %>  <div id="errorExplanation">    <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2>    <ul>    <% @post.errors.full_messages.each do |msg| %>      <li><%= msg %></li>    <% end %>    </ul>  </div>  <% end %>   <div class="field">    <%= f.label :name %>    <%= f.text_field :name %>  </div>  <div class="field">    <%= f.label :title %>    <%= f.text_field :title %>  </div>  <div class="field">    <%= f.label :content %>    <%= f.text_area :content %>  </div>  <div class="actions">    <%= f.submit %>  </div><% end %>

= form_for(@post) do |f| - if @post.errors.any? #errorExplanation %h2 = pluralize(@post.errors.count, "error") prohibited this post from being saved: %ul - @post.errors.full_messages.each do |msg| %li= msg .field = f.label :name = f.text_field :name .field = f.label :title = f.text_field :title .field = f.label :content = f.text_area :content .actions = f.submit

CSS versus SASShtml.rgba header#global nav .user-nav { background-color: rgba(0, 0, 0, 0.05);}

html.rgba header#global nav .user-nav .signin:active, html.rgba header#global nav .user-nav .signin:hover, html.rgba header#global nav .user-nav .signin:focus, html.rgba header#global nav .user-nav .signout:active, html.rgba header#global nav .user-nav .signout:hover, html.rgba header#global nav .user-nav .signout:focus { background-color: rgba(255, 255, 255, 0.5);}

html.no-rgba header#global nav .user-nav { background: url("/images/design/black-5.png");}

html.no-rgba header#global nav .user-nav .signin:active, html.no-rgba header#global nav .user-nav .signin:hover, html.no-rgba header#global nav .user-nav .signin:focus, html.no-rgba header#global nav .user-nav .signout:active, html.no-rgba header#global nav .user-nav .signout:hover, html.no-rgba header#global nav .user-nav .signout:focus { background-color: #edebe9;}

html &.rgba header#global nav .user-nav background-color: rgba(0, 0, 0, 0.05) .signin, .signout &:active, &:hover, &:focus background-color: rgba(255, 255, 255, 0.5)

&.no-rgba header#global nav .user-nav background: url("/images/design/black-5.png" .signin, .signout &:active, &:hover, &:focus background-color: #edebe9

SASS + Compass <3

.box +opacity(.5)

.box -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)" filter: alpha(opacity=50) opacity: .5

Advanced Compass

@import "my-icons/*.png".actions .new @include my-icons-sprite(new) .edit @include my-icons-sprite(edit) .save @include my-icons-sprite(save) .delete @include my-icons-sprite(delete)

.my-icons-sprite,

.actions .new,

.actions .edit,

.actions .save,

.actions .delete { background: url('/images/my-icons-s34fe0604ab.png') no-repeat; }

.actions .new { background-position: 0 -64px; }

.actions .edit { background-position: 0 -32px; }

.actions .save { background-position: 0 -96px; }

.actions .delete { background-position: 0 0; }

CoffeeScript

DI.Home = onload: -> @setupScrollable()

setupScrollable: -> $(".scrollable").scrollable( circular: true speed: 800 ).autoscroll( autoplay: true interval: 10000 ).navigator()

$ -> DI.Home.onload()

(function() { DI.Home = { onload: function() { return this.setupScrollable(); }, setupScrollable: function() { return $('.scrollable').scrollable({ circular: true, speed: 800 }).autoscroll({ autoplay: true, interval: 10000 }).navigator(); } }; $(function() { return DI.Home.onload(); });}).call(this);

Asset Management<%= javascript_include_tag "application" %>

<script src="/assets/application-908e25f4bf641868d8683022a5b62f54.js" type="text/javascript"></script>

<script src="/assets/core.js?body=1" type="text/javascript"></script><script src="/assets/projects.js?body=1" type="text/javascript"></script><script src="/assets/tickets.js?body=1" type="text/javascript"></script>

Rails Plugins

Rails Plugins•Authentication (Devise, OmniAuth, Facebook, Twitter)

•Storage (MySQL, PostgreSQL, MongoDB)

•Search (ElasticSearch, Sphinx)

•HTML (HAML, SASS, Less, Compass, Slim)

•Deployment (Capistrano, Heroku)

•Monitoring (Airbrake, NewRelic)

•Testing (RSpec, Cucumber, Capybara)

•JavaScript (jQuery, MooTools)

•...

PluginsI used to write functionality

Now I integrate functionality into solutions

RSpec for TDDrequire 'spec_helper'

describe Region do context 'with regions Sydney, North Sydney and Melbourne' do before do @sydney = Region.make(:sydney) @north_sydney = Region.make(:north_sydney) @melbourne = Region.make(:melbourne) end

describe '.find_by_postcode' do it 'should be Sydney for 2000' do gpo = Region.find_by_postcode('2000') gpo.should == @sydney end end endend

Cucumber for BDDFeature: Manage Articles In order to make a blog As an author I want to create and manage articles

Scenario: List articles Given I have articles titled Pizza, Breadsticks When I go to the list of article Then I should see "Pizza" And I should see "Breadsticks"

Scenario: Create an article Given I have no articles And I am on the list of articles When I follow "New Article" And I fill in "Title" with "Spuds" And I fill in "Content" with "Delicious potato wedges!" And I press "Create" Then I should see "New article created." And I should see "Spuds" And I should see "Delicious potato wedges!" And I should have 1 article

Steak for BDDrequire 'spec_helper'

feature 'Admin Sign In and Out', %q{ As an admin user I want to sign in and out So that I can access the admin section} do

background do AdminUser.make(:email => 'jdoe@protein-one.com', :password => 'password') end

scenario 'Valid admin login' do visit admin_path fill_in 'Email', :with => 'jdoe@protein-one.com' fill_in 'Password', :with => 'password' click_link 'Sign in' page.should have_content('Signed in successfully.') page.should have_css('a', :text => 'Sign out') end

scenario 'Invalid admin login' do visit admin_path fill_in 'Email', :with => 'jdoe@protein-one.com' fill_in 'Password', :with => 'wrong password' click_link 'Sign in' page.should have_content('Invalid email or password.') page.should_not have_content('Sign out') endend

Selenium Webdriver

Deployment# config/deploy.rbset :application, 'davidson'set :repository, 'git@git.protein-one.com'set :deploy_to, '/home/davidson/deployment'set :scm, :gitrole :app, '192.168.1.1', '192.168.1.2'role :web, '192.168.1.101', '192.168.1.102'role :db, '192.168.1.229', :primary => true

$ cap staging deploy ...

$ cap production deploy...

Can I Learn All This?

•Come pair program with me

•Ask me for my collection of Rails ebooks

•Pick an app to write

•Ask (shyness is niiiice but...)

•Nettuts+, Lynda.com, Treehouse

Rails at Protein One

•Let’s rapidly prototype our ideas

•Let’s focus on solutions, not code

•Let’s test and monitor so we can be suave and confident

Designers and Me

•If you give me Photoshop files

•I will use Compass (and spriting)

•If you give me HTML5

•I will convert it to HAML, SASS and CoffeeScript

•(so if you wanna save me time...)

Tagreg@gregorymcintyre.com

2013

top related