Slide 1

Slide 1 text

Taking the Red Pill the Ruby toolbox to start you down the rabbit hole Sunday, January 29, 12

Slide 2

Slide 2 text

Jamie Wright Brilliant Fantastic | [email protected] @jwright | Sunday, January 29, 12

Slide 3

Slide 3 text

Soon lat you’re going to realize, just as I did, that th e’s a diff ence between knowing the path and walking the path “ Sunday, January 29, 12

Slide 4

Slide 4 text

What to talk about today? Sunday, January 29, 12

Slide 5

Slide 5 text

Why Ruby? Sunday, January 29, 12

Slide 6

Slide 6 text

Why Ruby? Installation Sunday, January 29, 12

Slide 7

Slide 7 text

Web App Why Ruby? Installation Sunday, January 29, 12

Slide 8

Slide 8 text

Web App Why Ruby? Installation Gems Sunday, January 29, 12

Slide 9

Slide 9 text

Web App Why Ruby? Installation Gems Testing Sunday, January 29, 12

Slide 10

Slide 10 text

Web App API Why Ruby? Installation Gems Testing Sunday, January 29, 12

Slide 11

Slide 11 text

Web App API Why Ruby? Installation Gems Command Line Int face Testing Sunday, January 29, 12

Slide 12

Slide 12 text

Web App API Why Ruby? Installation Gems Command Line Int face Testing Gem Auth ing Sunday, January 29, 12

Slide 13

Slide 13 text

Web App API Why Ruby? Installation Gems Command Line Int face Testing Gem Auth ing Accepting Payments Sunday, January 29, 12

Slide 14

Slide 14 text

Web App API Why Ruby? Installation Gems Command Line Int face Testing Gem Auth ing Accepting Payments Deployment Sunday, January 29, 12

Slide 15

Slide 15 text

Sunday, January 29, 12

Slide 16

Slide 16 text

Why Ruby? Sunday, January 29, 12

Slide 17

Slide 17 text

Object Oriented Sunday, January 29, 12

Slide 18

Slide 18 text

Object Oriented class Alcoholic < Addict def substance puts “vodka” end end Sunday, January 29, 12

Slide 19

Slide 19 text

Object Oriented class Alcoholic < Addict def substance puts “vodka” end end class SpeedFreak < Addict def substance puts “crystal meth” end end Sunday, January 29, 12

Slide 20

Slide 20 text

Dynamic User.find_by_first_name_and_last_name “Jimmy”, “Page” Sunday, January 29, 12

Slide 21

Slide 21 text

Functional get ‘/contact’ do ... end Sunday, January 29, 12

Slide 22

Slide 22 text

Functional def get(path, options={}, &block) ... end get ‘/contact’ do ... end Sunday, January 29, 12

Slide 23

Slide 23 text

Beautiful Sunday, January 29, 12

Slide 24

Slide 24 text

Sunday, January 29, 12

Slide 25

Slide 25 text

5.years.ago Sunday, January 29, 12

Slide 26

Slide 26 text

5.years.ago 10.times { puts “Sexy” } Sunday, January 29, 12

Slide 27

Slide 27 text

5.years.ago 10.times { puts “Sexy” } users.each { |u| puts “Hello #{u.Name}” } Sunday, January 29, 12

Slide 28

Slide 28 text

5.years.ago 10.times { puts “Sexy” } users.each { |u| puts “Hello #{u.Name}” } var1 || “not null” Sunday, January 29, 12

Slide 29

Slide 29 text

Fl ible Sunday, January 29, 12

Slide 30

Slide 30 text

Modules & M ins class MyArray include Enumerable end Sunday, January 29, 12

Slide 31

Slide 31 text

Duck Typing animal.respond_to? ‘quack’ Sunday, January 29, 12

Slide 32

Slide 32 text

Open Objects class Numeric def plus(x) self.+(x) end end Sunday, January 29, 12

Slide 33

Slide 33 text

Open Objects class Numeric def plus(x) self.+(x) end end y = 5.plus 6 #y is now 11 Sunday, January 29, 12

Slide 34

Slide 34 text

Open Objects class Numeric def +(x) self - x end end Sunday, January 29, 12

Slide 35

Slide 35 text

Open Objects class Numeric def +(x) self - x end end y = 5 + 6 #y is now -1 sucker!!!! Sunday, January 29, 12

Slide 36

Slide 36 text

Popular Sunday, January 29, 12

Slide 37

Slide 37 text

Rails Sunday, January 29, 12

Slide 38

Slide 38 text

Sunday, January 29, 12

Slide 39

Slide 39 text

Web Framew k Sunday, January 29, 12

Slide 40

Slide 40 text

Web Framew k Active Rec d Sunday, January 29, 12

Slide 41

Slide 41 text

Web Framew k Active Rec d Template Engine Sunday, January 29, 12

Slide 42

Slide 42 text

Web Framew k Active Rec d Routing Template Engine Sunday, January 29, 12

Slide 43

Slide 43 text

Web Framew k Active Rec d Routing Template Engine Asset Pipeline Sunday, January 29, 12

Slide 44

Slide 44 text

Productive Sunday, January 29, 12

Slide 45

Slide 45 text

Installation Sunday, January 29, 12

Slide 46

Slide 46 text

Sunday, January 29, 12

Slide 47

Slide 47 text

RBENV RVM Sunday, January 29, 12

Slide 48

Slide 48 text

RBENV RVM Sunday, January 29, 12

Slide 49

Slide 49 text

Ruby Install Pik Sunday, January 29, 12

Slide 50

Slide 50 text

Ruby Install Pik Sunday, January 29, 12

Slide 51

Slide 51 text

Development Environment Sunday, January 29, 12

Slide 52

Slide 52 text

irb Sunday, January 29, 12

Slide 53

Slide 53 text

T tMate Sunday, January 29, 12

Slide 54

Slide 54 text

Sublime T t 2 Sunday, January 29, 12

Slide 55

Slide 55 text

MacVim Sunday, January 29, 12

Slide 56

Slide 56 text

RubyMine Sunday, January 29, 12

Slide 57

Slide 57 text

Web App Sunday, January 29, 12

Slide 58

Slide 58 text

Ge ing Started Sunday, January 29, 12

Slide 59

Slide 59 text

Rails Wizard Sunday, January 29, 12

Slide 60

Slide 60 text

Rails Wizard Sunday, January 29, 12

Slide 61

Slide 61 text

Ge ing Started Sunday, January 29, 12

Slide 62

Slide 62 text

Ge ing Started Sunday, January 29, 12

Slide 63

Slide 63 text

Gems Sunday, January 29, 12

Slide 64

Slide 64 text

M ale Gems #Gemfile source ‘http://rubygems.org’ gem ‘rails’ gem ‘haml’ gem ‘formtastic’ gem ‘rails_warden’ gem ‘paperclip’ gem ‘chronic’ gem ‘grape’ gem ‘aws-s3’ gem ‘cancan’ gem ‘chargify_api_ares’ gem ‘delayed_job’ gem ‘thor’ #Gemfile, con’t group :development, :test do gem ‘rspec-rails’ gem ‘factory_girl’ gem ‘capybara-webkit’ gem ‘database_cleaner’ gem ‘cucumber’ gem ‘pickle’ end Sunday, January 29, 12

Slide 65

Slide 65 text

Bundl Sunday, January 29, 12

Slide 66

Slide 66 text

Bundl #update your Gemfile > bundle install Sunday, January 29, 12

Slide 67

Slide 67 text

Bundl #update your Gemfile > bundle install #run a generator > bundle exec cucumber Sunday, January 29, 12

Slide 68

Slide 68 text

Database Driv s Sunday, January 29, 12

Slide 69

Slide 69 text

ActiveRec d class Account < ActiveRecord::Base belongs_to :plan belongs_to :owner, :class_name => ‘User’, :foreign_key => :owner_id has_many :assignments end Sunday, January 29, 12

Slide 70

Slide 70 text

ActiveRec d class Account < ActiveRecord::Base belongs_to :plan belongs_to :owner, :class_name => ‘User’, :foreign_key => :owner_id has_many :assignments end account = Account.find_by_site_address ‘bf’ account.assignments.where(:user_id => current_user.id) Sunday, January 29, 12

Slide 71

Slide 71 text

Updating a Model class AddAllowApiAccessToAccount < ActiveRecord::Migration def self.up add_column :accounts, :allow_api_access, :boolean end def self.down remove_column :accounts, :allow_api_access end end Sunday, January 29, 12

Slide 72

Slide 72 text

Updating a Model class AddAllowApiAccessToAccount < ActiveRecord::Migration def self.up add_column :accounts, :allow_api_access, :boolean end def self.down remove_column :accounts, :allow_api_access end end account = Account.find_by_site_address ‘bf’ account.allow_api_access = true Sunday, January 29, 12

Slide 73

Slide 73 text

Qu y Int face Sunday, January 29, 12

Slide 74

Slide 74 text

Qu y Int face account = Account.first Sunday, January 29, 12

Slide 75

Slide 75 text

Qu y Int face account = Account.first account = Account.find 10 Sunday, January 29, 12

Slide 76

Slide 76 text

Qu y Int face account = Account.first account = Account.find 10 accounts = Account.all Sunday, January 29, 12

Slide 77

Slide 77 text

Qu y Int face account = Account.first account = Account.find 10 accounts = Account.all accounts = Account.where(‘status = ?’, ‘past_due’) Sunday, January 29, 12

Slide 78

Slide 78 text

Qu y Int face account = Account.first account = Account.find 10 accounts = Account.all accounts = Account.where(‘status = ?’, ‘past_due’) accounts = Account.where(‘created_at >= :start_date AND created_at < :end_date’, {:start_date => 2.days.ago, :end_date => 1.day.ago}) Sunday, January 29, 12

Slide 79

Slide 79 text

Sunday, January 29, 12

Slide 80

Slide 80 text

Templating Engines Sunday, January 29, 12

Slide 81

Slide 81 text

Haml .main-container-title %h1 My Account .main-container-inner = render :partial => "shared/success" = render :partial => "shared/errors", :object => @account, :as => :model .sub-title Change settings for my account .content = semantic_form_for @account do |form| .form-area.span-9 = form.input :group_name, :label => "Account name", :input_html => { :class => "focusable" } .form-area.span-4 = form.input :site_address, :label => "Subdomain", :input_html => { :style => "width: 100px;", :onkeyup => "Accounts.copySiteAddress();" } .command-area = link_to "Cancel my account", cancel_account_path = form.commit_button Sunday, January 29, 12

Slide 82

Slide 82 text

Haml .main-container-title %h1 My Account .main-container-inner = render :partial => "shared/success" = render :partial => "shared/errors", :object => @account, :as => :model .sub-title Change settings for my account .content = semantic_form_for @account do |form| .form-area.span-9 = form.input :group_name, :label => "Account name", :input_html => { :class => "focusable" } .form-area.span-4 = form.input :site_address, :label => "Subdomain", :input_html => { :style => "width: 100px;", :onkeyup => "Accounts.copySiteAddress();" } .command-area = link_to "Cancel my account", cancel_account_path = form.commit_button

My Account

Sunday, January 29, 12

Slide 83

Slide 83 text

Haml .main-container-title %h1 My Account .main-container-inner = render :partial => "shared/success" = render :partial => "shared/errors", :object => @account, :as => :model .sub-title Change settings for my account .content = semantic_form_for @account do |form| .form-area.span-9 = form.input :group_name, :label => "Account name", :input_html => { :class => "focusable" } .form-area.span-4 = form.input :site_address, :label => "Subdomain", :input_html => { :style => "width: 100px;", :onkeyup => "Accounts.copySiteAddress();" } .command-area = link_to "Cancel my account", cancel_account_path = form.commit_button Account name: Sunday, January 29, 12

Slide 84

Slide 84 text

An h choice... Sunday, January 29, 12

Slide 85

Slide 85 text

My Account

<%= render :partial => "shared/success" %> <% = render :partial => "shared/errors", :object => @account, :as => :model %>
Change settings for my account
<% = semantic_form_for @account do |form| %>
<% = form.input :group_name, :label => "Account name", :input_html => { :class => "focusable" } %>
<% = form.input :site_address, :label => "Subdomain", :input_html => { :style => "width: 100px;", :onkeyup => "Accounts.copySiteAddress();" } %>
<% = link_to "Cancel my account", cancel_account_path %> <% = form.commit_button %>
<%= end %>
ERB Sunday, January 29, 12

Slide 86

Slide 86 text

Testing Li aries Sunday, January 29, 12

Slide 87

Slide 87 text

RSpec describe "String" do describe "#random" do it "should not include excluded characters" do excluded = ["0", "O", "o", "I", "i", "1"] 1.upto 1000 do |i| String.random(10, excluded).split(//).should_not include excluded end end end end Sunday, January 29, 12

Slide 88

Slide 88 text

RSpec describe "String" do describe "#random" do it "should not include excluded characters" do excluded = ["0", "O", "o", "I", "i", "1"] 1.upto 1000 do |i| String.random(10, excluded).split(//).should_not include excluded end end end end String#random should not include excluded characters Finished in 0.007546 seconds 1 example, 0 failures Sunday, January 29, 12

Slide 89

Slide 89 text

An h choice... Sunday, January 29, 12

Slide 90

Slide 90 text

class RandomStringTest < Test::Unit::TestCase def should_not_include_excluded_characters excluded = ["0", "O", "o", "I", "i", "1"] 1.upto 1000 do |i| assert !String.random(10, excluded).split(//).include? excluded end end end Test Unit Sunday, January 29, 12

Slide 91

Slide 91 text

class RandomStringTest < Test::Unit::TestCase def should_not_include_excluded_characters excluded = ["0", "O", "o", "I", "i", "1"] 1.upto 1000 do |i| assert !String.random(10, excluded).split(//).include? excluded end end end Test Unit Started . Finished in 0.007546 seconds 1 tests, 1 assertion, 0 failures, 0 errors Sunday, January 29, 12

Slide 92

Slide 92 text

Top-down Testing Sunday, January 29, 12

Slide 93

Slide 93 text

Top-down D esign Sunday, January 29, 12

Slide 94

Slide 94 text

Cucumb Feature: Updating an account In order to update billing information or add/remove projects As an account owner I need to be able to update the account settings Scenario: Removing a project from an account that is not the last one Given I am authenticated with project: "Testing Project", role: "Account Owner" And a project exists with name: "Testing Project #2" And I am on the account edit page When I follow "Delete Testing Project #2" Then I should not see "Testing Project #2" within ".project-detail" And I should see "You removed the project Testing Project #2 from the Testing Account account." Sunday, January 29, 12

Slide 95

Slide 95 text

Using the default profile... Feature: Updating an account In order to update billing information or add/remove projects As an account owner I need to be able to update the account settings Scenario: Removing a project from an account that is not the last one # features/account_page.feature:26 Given I am authenticated with project: "Testing Project", role: "Account Owner" # features/step_definitions/ account_steps.rb:21 And a project exists with name: "Testing Project #2" # features/step_definitions/pickle_steps.rb:4 And I am on the account edit page # features/step_definitions/web_steps.rb:19 When I follow "Delete Testing Project #2" # features/step_definitions/web_steps.rb:33 Then I should not see "Testing Project #2" within ".project-detail" # features/step_definitions/ web_steps.rb:128 And I should see "You removed the project Testing Project #2 from the Testing Account account." # features/ step_definitions/web_steps.rb:107 1 scenario (1 passed) 7 steps (7 passed) 0m3.881s Cucumb Sunday, January 29, 12

Slide 96

Slide 96 text

Write Scenarios Sunday, January 29, 12

Slide 97

Slide 97 text

Write Scenarios Steps are pending Sunday, January 29, 12

Slide 98

Slide 98 text

Write Scenarios Steps are pending Write Step Definitions Sunday, January 29, 12

Slide 99

Slide 99 text

Write Scenarios Steps are pending Write Step Definitions Go down to RSpec Sunday, January 29, 12

Slide 100

Slide 100 text

Write Scenarios Steps are pending Write Step Definitions Go down to RSpec Write failing test Sunday, January 29, 12

Slide 101

Slide 101 text

Write Scenarios Steps are pending Write Step Definitions Go down to RSpec Write failing test Make example pass Sunday, January 29, 12

Slide 102

Slide 102 text

Write Scenarios Steps are pending Write Step Definitions Go down to RSpec Write failing test Make example pass Go back up to Cucumber Sunday, January 29, 12

Slide 103

Slide 103 text

Write Scenarios Steps are pending Write Step Definitions Go down to RSpec Write failing test Make example pass Go back up to Cucumber Make steps pass Sunday, January 29, 12

Slide 104

Slide 104 text

Write Scenarios Steps are pending Refactor! Write Step Definitions Go down to RSpec Write failing test Make example pass Go back up to Cucumber Make steps pass Sunday, January 29, 12

Slide 105

Slide 105 text

Ge ing Paid Sunday, January 29, 12

Slide 106

Slide 106 text

Sunday, January 29, 12

Slide 107

Slide 107 text

Chargify require 'chargify_api_ares' class Subscription < PaymentProcessor include ActiveModel::Dirty include ActiveModel::Validations define_attribute_methods [:first_name, :last_name, :email, :card_number, :expiration_month, :expiration_year, :plan] validates_presence_of :first_name, :last_name, :email, :plan validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i validates_inclusion_of :expiration_month, :in => 1..12, :allow_nil => true, :message => "is not a valid month." validates_numericality_of :expiration_year, :only_integer => true, :allow_nil => true, :message => "is not a valid year." def save_customer! begin customer = Chargify::Customer.find_by_reference @reference rescue ActiveResource::ResourceNotFound customer = Chargify::Customer.new( :reference => @reference ) end customer.first_name = @first_name customer.last_name = @last_name customer.email = @email customer.save! end end Sunday, January 29, 12

Slide 108

Slide 108 text

An h choice... Sunday, January 29, 12

Slide 109

Slide 109 text

An h choice... S ipe Sunday, January 29, 12

Slide 110

Slide 110 text

Deployment Sunday, January 29, 12

Slide 111

Slide 111 text

Sunday, January 29, 12

Slide 112

Slide 112 text

H oku Sunday, January 29, 12

Slide 113

Slide 113 text

Sunday, January 29, 12

Slide 114

Slide 114 text

Writing an API Sunday, January 29, 12

Slide 115

Slide 115 text

Grape Sunday, January 29, 12

Slide 116

Slide 116 text

Grape require 'grape' module Morale class API < Grape::API error_format :json prefix 'api' version 'v1' resources :projects do http_basic do |u,p| !User.authenticate_by_api_key(u, p).nil? end helpers do def project @project ||= Project.find_by_id(params[:id]) end end # GET /api/v1/projects get do account.projects end .... Sunday, January 29, 12

Slide 117

Slide 117 text

require 'grape' module Morale class API < Grape::API error_format :json prefix 'api' version 'v1' resources :projects do http_basic do |u,p| !User.authenticate_by_api_key(u, p).nil? end helpers do def project @project ||= Project.find_by_id(params[:id]) end end # GET /api/v1/projects get do account.projects end .... # GET /api/v1/projects/1 get '/:id' do error!("Project does not exist", 404) if project.nil? project end # PUT /api/v1/projects/1 put '/:id' do error!("Project does not exist", 404) if project.nil? project.update_attributes!(params[:project]) project end # POST /api/v1/projects post do account = validate_account project = Project.new(params[:project]) end # DELETE /api/v1/projects/1 delete '/:id' do error!("Project does not exist", 404) if project.nil? project.delete project.save! project end Sunday, January 29, 12

Slide 118

Slide 118 text

API curl --user api-test:XfADs6yw0qshq2Svaf https://api- test.teammorale.com/api/v1/projects Sunday, January 29, 12

Slide 119

Slide 119 text

API curl --user api-test:XfADs6yw0qshq2Svaf https://api- test.teammorale.com/api/v1/projects curl --user api-test:XfADs6yw0qshq2Svaf --data "{'project': {'name':'Test Project #2'}}" -X POST https://api-test.teammorale.com/ api/v1/projects Sunday, January 29, 12

Slide 120

Slide 120 text

API curl --user api-test:XfADs6yw0qshq2Svaf https://api- test.teammorale.com/api/v1/projects curl --user api-test:XfADs6yw0qshq2Svaf --data "{'project': {'name':'Test Project #2'}}" -X POST https://api-test.teammorale.com/ api/v1/projects curl --user api-test:XfADs6yw0qshq2Svaf -X DELETE https://api- test.teammorale.com/api/v1/projects/1 Sunday, January 29, 12

Slide 121

Slide 121 text

Command Line Int face Sunday, January 29, 12

Slide 122

Slide 122 text

M ale CLI Sunday, January 29, 12

Slide 123

Slide 123 text

Th Sunday, January 29, 12

Slide 124

Slide 124 text

require 'thor' module Morale class Command < Thor desc "login", "Signs a user in using their email address and password. Stores the users API key locally to use for access later." def login Morale::Commands::Authorization.login end desc "accounts [EMAIL]", "Gets all the subdomains available for a given email address if provided, else it uses the current api key." method_options :change => false def accounts(email="") Morale::Commands::Account.list email, options.change end desc "accounts ID", "Changes the current account to the numeric identifier of the account specified." def account(id) Morale::Commands::Account.select id end desc "projects", "Lists the projects available to the user and the current account." method_options :change => false def projects Morale::Commands::Project.list options.change end Th Sunday, January 29, 12

Slide 125

Slide 125 text

Testing the CLI Sunday, January 29, 12

Slide 126

Slide 126 text

uba Sunday, January 29, 12

Slide 127

Slide 127 text

Feature: Running the projects command In order to view and select a project to work with As a command line user of Morale I should be able to run projects to view available projects and store my selected project information locally @interactive Scenario: Running projects should not require authorization if the account and api key are stored Given I am authenticated When I run `morale projects` interactively Then the output should contain: """ 1. Skunk Works 2. Spin Free Project """ @interactive Scenario: Running projects with the --change option should ask me to change the project Given I am authenticated When I run `morale projects --change` interactively Then the output should contain: """ Choose a project: """ uba Sunday, January 29, 12

Slide 128

Slide 128 text

Writing a gem Sunday, January 29, 12

Slide 129

Slide 129 text

M ale Gem Sunday, January 29, 12

Slide 130

Slide 130 text

Gem::Specification.new do |s| s.specification_version = 2 if s.respond_to? :specification_version= s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.rubygems_version = '1.3.5' s.name = 'morale' s.version = '1.1.1' s.date = '2011-12-12' s.rubyforge_project = 'morale' s.summary = "Command line interface to create & manage tickets on Morale." s.description = "Client library and command-line tool to manage tickets and control your account on Morale." s.authors = ["Brilliant Fantastic"] s.email = '[email protected]' s.homepage = 'http://teammorale.com' s.require_paths = %w[lib] s.executables = ["morale"] s.rdoc_options = ["--charset=UTF-8"] s.extra_rdoc_files = %w[README.md] s.add_dependency('morale-client', "~> 0.0.1") s.add_dependency('hirb', "~> 0.5.0") s.add_dependency('json', "~> 1.4.6") s.add_dependency('thor', "~> 0.14.6") M ale GemSpec Sunday, January 29, 12

Slide 131

Slide 131 text

Deploying a Gem Sunday, January 29, 12

Slide 132

Slide 132 text

Sunday, January 29, 12

Slide 133

Slide 133 text

desc "Create tag v#{version} and build and push #{gem_file} to Rubygems" task :release => :build do unless `git branch` =~ /^\* master$/ puts "You must be on the master branch to release!" exit! end sh "git commit --allow-empty -a -m 'Release #{version}'" sh "git tag v#{version}" sh "git push origin master" sh "git push origin v#{version}" sh "gem push pkg/#{name}-#{version}.gem" end desc "Build #{gem_file} into the pkg directory" task :build => :gemspec do sh "mkdir -p pkg" sh "gem build #{gemspec_file}" sh "mv #{gem_file} pkg" Rake GemSpec Sunday, January 29, 12

Slide 134

Slide 134 text

I imagine that right now, you’re feeling a bit like Alice. Hmm? Tumbling down the rabbit hole? “ Sunday, January 29, 12

Slide 135

Slide 135 text

Don’t y and bend the spoon. That’s impossible. Instead y and realize the uth… Th e is no spoon. Then you see that it is n the spoon that bends, it’s only yourself. “ “ Sunday, January 29, 12

Slide 136

Slide 136 text

Thanks! Sunday, January 29, 12

Slide 137

Slide 137 text

Jamie Wright Brilliant Fantastic | [email protected] @jwright | Sunday, January 29, 12