136
RUBY ON RAILS

Rails workshop for Java people (September 2015)

Embed Size (px)

Citation preview

Page 1: Rails workshop for Java people (September 2015)

RUBY ON RAILS

Page 2: Rails workshop for Java people (September 2015)

Ruby vs Java

Page 3: Rails workshop for Java people (September 2015)

Ruby = Slower

Page 4: Rails workshop for Java people (September 2015)

Java = Verbose

Page 5: Rails workshop for Java people (September 2015)

Ruby == JavaWhat's the same?

Page 6: Rails workshop for Java people (September 2015)

Garbage collected

Page 7: Rails workshop for Java people (September 2015)

Strongly typed objects

Page 8: Rails workshop for Java people (September 2015)

public, private, and protected

Page 9: Rails workshop for Java people (September 2015)

Ruby != JavaWhat's different?

Page 10: Rails workshop for Java people (September 2015)

No compilation

Page 11: Rails workshop for Java people (September 2015)

begin...end vs {}

Page 12: Rails workshop for Java people (September 2015)

require instead of import

Page 13: Rails workshop for Java people (September 2015)

Parentheses optional

in method calls

Page 14: Rails workshop for Java people (September 2015)

Everything is an object

Page 15: Rails workshop for Java people (September 2015)

No types, no casting, no static type checking

Page 16: Rails workshop for Java people (September 2015)

Foo.new vs new Foo()

Page 17: Rails workshop for Java people (September 2015)

nil == null

Page 18: Rails workshop for Java people (September 2015)

under_score vs CamelCase

Page 19: Rails workshop for Java people (September 2015)

No method overloading

Page 20: Rails workshop for Java people (September 2015)

Mixins vs interfaces

Page 21: Rails workshop for Java people (September 2015)

Example time

Page 22: Rails workshop for Java people (September 2015)

Classes

Page 23: Rails workshop for Java people (September 2015)

class Person attr_accessor :name

def initialize(name) @name = name end

def say(message="default message") puts "#{name}: #{message}" unless message.blank? endend

Page 24: Rails workshop for Java people (September 2015)

> andre = Person.new("André")> andre.say("Hello!")=> André: Hello!

Page 25: Rails workshop for Java people (September 2015)

Inheritance

Page 26: Rails workshop for Java people (September 2015)

class RudePerson < Person def shout(message="default message") say(message.upcase) endend

Page 27: Rails workshop for Java people (September 2015)

Mixins

Page 28: Rails workshop for Java people (September 2015)

module RudeBehaviour def shout(message="default message") say(message.upcase) endend

class Person include RudeBehaviour # ...end

Page 29: Rails workshop for Java people (September 2015)

Readability nazi'sUsually 5+ ways to do the same thing, so it looks nice.

Page 30: Rails workshop for Java people (September 2015)

def repeat(message="default message", count=1) # say message count times.end

Page 31: Rails workshop for Java people (September 2015)

def repeat(message="default message", count=1) loop do say(message) count -= 1 break if count == 0 endend

Page 32: Rails workshop for Java people (September 2015)

def repeat(message="default message", count=1) while(count > 0) say(message) count -= 1 endend

Page 33: Rails workshop for Java people (September 2015)

def repeat(message="default message", count=1) until(count == 0) say(message) count -= 1 endend

Page 34: Rails workshop for Java people (September 2015)

def repeat(message="default message", count=1) (0...count).each do say(message) endend

Page 35: Rails workshop for Java people (September 2015)

def repeat(message="default message", count=1) for index in (0...count) say(message) endend

Page 36: Rails workshop for Java people (September 2015)

def repeat(message="default message", count=1) count.times do say(message) endend

Page 37: Rails workshop for Java people (September 2015)

Meta-programmingChanging the code on the fly

Page 38: Rails workshop for Java people (September 2015)

> Person.new.respond_to?(:shout)=> false> Person.include(RudeBehaviour)=> Person> Person.new.respond_to?(:shout)=> true

Page 39: Rails workshop for Java people (September 2015)

# Add the 'shout' method to each object that has a 'say' method.> Object.subclasses.each{ |o| o.include(RudeBehaviour) if o.respond_to?(:say) }

Page 40: Rails workshop for Java people (September 2015)

We can manipulate code

Page 41: Rails workshop for Java people (September 2015)

class Person ["hello", "goodbye", "howdie"].each do |word| define_method("say_#{word}") do say("#{name}: #{word}") end endend

Page 42: Rails workshop for Java people (September 2015)

> Person.new("André").say_hello=> André: hello

Page 43: Rails workshop for Java people (September 2015)

Who even needs methods?The power of method_missing

Page 44: Rails workshop for Java people (September 2015)

class Person def method_missing(method_name, *arguments) if method_name.starts_with?("say_") # Say everything after 'say_' say(method_name[4..-1]) else super end endend

Page 45: Rails workshop for Java people (September 2015)

Your turn

Page 46: Rails workshop for Java people (September 2015)

Animal kingdomBuild a Fish, Chicken, and Platypus

Page 47: Rails workshop for Java people (September 2015)

4 Each animal makes a noise (blub, tock, gnarl)

4 All animals have health

4 Certain animals have a beak (chicken, platypus, NOT fish)

4 Animals with a beak can peck other animals (health--)

4 Certain animals can lay eggs (fish, chicken, NOT platypus)

Page 48: Rails workshop for Java people (September 2015)

module RudeBehaviour def shout(message="default message") say(message.upcase) endend

class Person include RudeBehaviour

attr_accessor :name

def initialize(name) self.name = name end

def say(message="default message") puts "#{name}: #{message}" unless message.blank? endend

Page 49: Rails workshop for Java people (September 2015)

class Animal attr_accessor :health

def initialize self.health = 100 end

def make_noise noise puts noise endend

module Beaked def peck(animal) animal.health -= 1 endend

class Egg ; end

module EggLayer def lay_egg Egg.new endend

class Chicken < Animal include Beaked, EggLayer def make_noise super("tock") endend

Page 50: Rails workshop for Java people (September 2015)

RailsA web framework

Page 51: Rails workshop for Java people (September 2015)

DRY & MVCConvention over configuration

Page 52: Rails workshop for Java people (September 2015)

[EXPLAIN MVC GRAPH]

Page 53: Rails workshop for Java people (September 2015)

GET http://localhost:3000/people

Page 54: Rails workshop for Java people (September 2015)

Routing# config/routes.rbMyApp::Application.routes.draw do get "/people", to: "people#index"end

Page 55: Rails workshop for Java people (September 2015)

The Controller# app/controllers/people_controller.rbclass PeopleController < ApplicationController def index endend

Page 56: Rails workshop for Java people (September 2015)

The view# app/views/people/index.html<p>Hello world!</p>

Page 57: Rails workshop for Java people (September 2015)

This is a bit plain...Let's add some dynamic data

Page 58: Rails workshop for Java people (September 2015)

The Controller# app/controllers/people_controller.rbclass PeopleController < ApplicationController def index @people = ["André", "Pieter", "Matthijs"] endend

Page 59: Rails workshop for Java people (September 2015)

The view (ERB)# app/views/people/index.html.erb<ul><% @people.each do |person| %> <li><%= person %></li><% end %></ul>

Page 60: Rails workshop for Java people (September 2015)

The view (SLIM)# app/views/people/index.html.slimul - @people.each do |person| li= person

Page 61: Rails workshop for Java people (September 2015)

What about a database?The default is SQLLite

Page 62: Rails workshop for Java people (September 2015)

The model# app/models/person.rbclass Person < ActiveRecord::Baseend

Page 63: Rails workshop for Java people (September 2015)

Migrations# db/migrations/00000000_create_people.rbclass CreatePeople < ActiveRecord::Migration def change create_table :people do |t| t.string :name end endend

rails g migration create_people name:string

Page 64: Rails workshop for Java people (September 2015)

Wait!!!?How does it know people belongs to the person model?

Page 65: Rails workshop for Java people (September 2015)

Convention over configuration

Tables are plural, models are singular

Page 66: Rails workshop for Java people (September 2015)

> rake db:create db:migrate

Page 67: Rails workshop for Java people (September 2015)

The Controller# app/controllers/people_controller.rbclass PeopleController < ApplicationController def index @people = Person.all endend

Page 68: Rails workshop for Java people (September 2015)

The view (SLIM)# app/views/people/index.html.slimul - @people.each do |person| li= person.name

Page 69: Rails workshop for Java people (September 2015)

Convention over configuration

All columns are mapped to methods

Page 70: Rails workshop for Java people (September 2015)

Okay, lets add some people.

Page 71: Rails workshop for Java people (September 2015)

> Person.new(name: "André").save> Person.new(name: "Pieter").save> Person.new(name: "Matthijs").save

> Person.count=> 3

Page 72: Rails workshop for Java people (September 2015)

Validations> Person.new.save=> true

Page 73: Rails workshop for Java people (September 2015)

The model# app/models/person.rbclass Person < ActiveRecord::Base validates :name, presence: trueend

Page 74: Rails workshop for Java people (September 2015)

Validations> p = Person.new

> p.save=> false

> p.errors.messages=> {:name=>["can't be blank"]}

Page 75: Rails workshop for Java people (September 2015)

Let's add a formThis will introduce two new 'actions'

Page 76: Rails workshop for Java people (September 2015)

NEW & CREATEthe form, and the creation

Page 77: Rails workshop for Java people (September 2015)

Routing# config/routes.rbMyApp::Application.routes.draw do get "/people", to: "people#index" get "/people/new", to: "people#new" post "/people", to: "people#create"end

Page 78: Rails workshop for Java people (September 2015)

Routing# config/routes.rbMyApp::Application.routes.draw do resources :people, only: [:index, :new, :create]end

Page 79: Rails workshop for Java people (September 2015)

Convention over configuration

index, show, new, edit, create, update, destroy

Page 80: Rails workshop for Java people (September 2015)

The Controller# app/controllers/people_controller.rbclass PeopleController < ApplicationController def index @people = Person.all end

def new @person = Person.new endend

Page 81: Rails workshop for Java people (September 2015)

The view (ERB)# app/views/people/new.html.erb<%= form_for @person do |f| %> <div> <%= f.text_field :name %> </div> <div> <%= f.submit "Save" %> </div><% end %>

Page 82: Rails workshop for Java people (September 2015)

The view (SLIM)# app/views/people/new.html.slim= form_for @person do |f| div= f.text_field :name div= f.submit "Save"

From now on, we will continue in SLIM, but ERB is just as good.

Page 83: Rails workshop for Java people (September 2015)

The Controller# app/controllers/people_controller.rbclass PeopleController < ApplicationController # def index ... # def new ...

def create @person = Person.new(person_attributes) if @person.save redirect_to action: :index else render :new end end

private

def person_attributes params.require(:person).permit(:name) endend

Page 84: Rails workshop for Java people (September 2015)

The view (SLIM)# app/views/people/new.html.slim- @person.errors.full_messages.each do |error| div.red= error

= form_for @person do |f| div= f.text_field :name div= f.submit "Save"

Page 85: Rails workshop for Java people (September 2015)

Finally, destroying stuff.

Page 86: Rails workshop for Java people (September 2015)

Routing# config/routes.rbMyApp::Application.routes.draw do resources :people, only: [:index, :new, :create, :destroy]end

Page 87: Rails workshop for Java people (September 2015)

The view (SLIM)# app/views/people/index.html.slimul - @people.each do |person| li= link_to(person.name, person_path(person), method: :delete)

NOTE: the method is the HTTP method, not the controller method.

Page 88: Rails workshop for Java people (September 2015)

Path helpers> rake routesPrefix Verb URI Pattern Controller#Action-------------------------------------------------------------people GET /people people#indexperson GET /people/:id people#shownew_person GET /people/new people#newedit_person GET /people/:id/edit people#edit POST /people people#create PATCH /people/:id people#update DELETE /people/:id people#destroy

Page 89: Rails workshop for Java people (September 2015)

The Controller# app/controllers/people_controller.rbclass PeopleController < ApplicationController # def index ... # def new ... # def create ...

def destroy Person.find(params[:id]).destroy redirect_to action: :index endend

Page 90: Rails workshop for Java people (September 2015)

Example time

Page 91: Rails workshop for Java people (September 2015)

Installing ruby (OSX)> brew install rbenv ruby-build

Then add eval "$(rbenv init -)" to your .profile

> rbenv install 2.2.3

Linux: https://github.com/sstephenson/rbenv

Page 92: Rails workshop for Java people (September 2015)

Installing Rails> gem install rails

Page 93: Rails workshop for Java people (September 2015)

Making your app> rails new [my_app]

Page 94: Rails workshop for Java people (September 2015)

Structure- app - models - views - controllers- config- db- Gemfile / Gemfile.lock

Page 95: Rails workshop for Java people (September 2015)

Dependencies# Gemfilegem 'slim-rails'

Add this line to your Gemfile to use slim, then install them:

> bundle install

Page 96: Rails workshop for Java people (September 2015)

Running your app> rails s

Page 97: Rails workshop for Java people (September 2015)

Build a small app (45 mins)Use the pdf for reference

Page 98: Rails workshop for Java people (September 2015)

Your app should:

4 be able to add items

4 be able to edit items

4 be able to destroy items

4 be able to show a single item

4 be able to show a list of items

Page 99: Rails workshop for Java people (September 2015)

Next up: relationshas_many, belongs_to, has_one, has_many_through

Page 100: Rails workshop for Java people (September 2015)

The model# app/models/person.rbclass Person < ActiveRecord::Base validates :name, presence: true has_many :skillsend

# app/models/skill.rbclass Skill < ActiveRecord::Base belongs_to :personend

Page 101: Rails workshop for Java people (September 2015)

Migrations# db/migrations/00000000_create_skills.rbclass CreateSkills < ActiveRecord::Migration def change create_table :skills do |t| t.string :name t.references :person end endend

Page 102: Rails workshop for Java people (September 2015)

But this binds the skill to a single person...

Page 103: Rails workshop for Java people (September 2015)

We need a link tableRails forces you to name it properly!

Page 104: Rails workshop for Java people (September 2015)

# app/models/proficiency.rbclass Proficiency < ActiveRecord::Base belongs_to :person belongs_to :skillend

Page 105: Rails workshop for Java people (September 2015)

Migrations# db/migrations/00000000_create_proficiencies.rbclass CreateProficiencies < ActiveRecord::Migration def change create_table :proficiencies do |t| t.references :person t.references :skill end endend

Page 106: Rails workshop for Java people (September 2015)

Migrations# db/migrations/00000000_remove_person_reference_from_skills.rbclass RemovePersonReferenceFromSkills < ActiveRecord::Migration def change remove_reference :skills, :person endend

Page 107: Rails workshop for Java people (September 2015)

The model# app/models/person.rbclass Person < ActiveRecord::Base validates :name, presence: true

has_many :proficiencies has_many :skills, through: :proficienciesend

# app/models/skill.rbclass Skill < ActiveRecord::Base has_many :proficiencies has_many :people, through: :proficienciesend

# app/models/proficiency.rbclass Proficiency < ActiveRecord::Base belongs_to :person belongs_to :skillend

Page 108: Rails workshop for Java people (September 2015)

> andre = Person.new(name: "André")> andre.skills << Skill.create(name: "Knitting")> andre.save

Page 109: Rails workshop for Java people (September 2015)

Update your app (30 mins)Use the pdf for reference

Page 110: Rails workshop for Java people (September 2015)

Your app should:

4 have a relationship through a link table

4 have the forms to create/update/destroy the related items (in our example: Skills)

4 should NOT be able to build the relationship using a form (yet).

Page 111: Rails workshop for Java people (September 2015)

Building nested formsThough usually it can be prevented by making the link table a first-class citizen.

Page 112: Rails workshop for Java people (September 2015)

The view# app/views/people/new.html.slim= form_for @person do |f| div= f.text_field :name = f.fields_for :proficiencies, @person.proficiencies.build do |g| div= g.collection_select :skill_id, Skill.all, :id, :name div= f.submit "Save"

Page 113: Rails workshop for Java people (September 2015)

The controller# app/controllers/people_controller.rbclass PeopleController < ApplicationController # def index ... # def new ... # def create ...

private

def person_attributes params.require(:person).permit(:name, proficiencies_attributes: [:skill_id]) endend

Page 114: Rails workshop for Java people (September 2015)

The model# app/models/person.rbclass Person < ActiveRecord::Base validates :name, presence: true

has_many :proficiencies has_many :skills, through: :proficiencies

accepts_nested_attributes_for :proficienciesend

Page 115: Rails workshop for Java people (September 2015)

Update your app (30 mins)Use the pdf for reference

Page 116: Rails workshop for Java people (September 2015)

Your app should:

4 should be able to build the relationship using a form.

4 you can pick: nested or first-class

Page 117: Rails workshop for Java people (September 2015)

TestingPick your poison: rspec, test-unit,

Page 118: Rails workshop for Java people (September 2015)

# test/models/person_test.rbrequire 'test_helper'

class PersonTest < ActiveSupport::TestCase test "Person has a name, that is required" do assert !Person.new.valid? assert Person.new(name: "André").valid? endend

Run your tests

> rake test

Page 119: Rails workshop for Java people (September 2015)

# test/integration/people_get_test.rbrequire 'test_helper'

class PeopleGetTest < ActionDispatch::IntegrationTest test "that the index shows a list of people" do # Build three people names = ["André", "Matthijs", "Pieter"] names.each{ |name| Person.create(name: name) }

get people_path assert_response :success

assert_select "li", "André" assert_select "li", "Matthijs" assert_select "li", "Pieter" endend

Page 120: Rails workshop for Java people (September 2015)

Update your app (15 mins)Use the pdf for reference

Page 121: Rails workshop for Java people (September 2015)

Your app should:

4 Test your validations, and relationships.

4 Test a few basic forms

Page 122: Rails workshop for Java people (September 2015)

Building API'sMaking a JSON API for your models

Page 123: Rails workshop for Java people (September 2015)

Routing# config/routes.rbMyApp::Application.routes.draw do resources :people namespace :api do resources :people endend

Page 124: Rails workshop for Java people (September 2015)

The controller# app/controllers/api/people_controller.rbclass Api::PeopleController < ApplicationController def index render json: Person.all endend

Page 125: Rails workshop for Java people (September 2015)

The response[ { id: 1, name: "André" }, { id: 2, name: "Pieter" }, { id: 3, name: "Matthijs" } ]

Page 126: Rails workshop for Java people (September 2015)

Adapting the JSONMultiple ways to achieve the same thing.

Page 127: Rails workshop for Java people (September 2015)

The model# app/models/person.rbclass Person < ActiveRecord::Base def as_json options={} { name: name } endend

Page 128: Rails workshop for Java people (September 2015)

But this changes it everywhere

JSON is also a view

Page 129: Rails workshop for Java people (September 2015)

The controller# app/controllers/api/people_controller.rbclass Api::PeopleController < ApplicationController def index @people = Person.all endend

Page 130: Rails workshop for Java people (September 2015)

The view# app/views/api/people/index.json.jbuilderjson.array! @people, :name

Page 131: Rails workshop for Java people (September 2015)

This means we can even merge both controllers

just drop the jbuilder view in the original views

the view will be selected using the request format

Page 132: Rails workshop for Java people (September 2015)

Update your app (45 mins)Use the pdf for reference

Page 133: Rails workshop for Java people (September 2015)

Your app should:

4 Have a view JSON api

4 Test the API

Page 134: Rails workshop for Java people (September 2015)

Q & ASpecific questions go here

Page 135: Rails workshop for Java people (September 2015)

EXTRA: Update your app (60 mins)

Use the pdf for reference

Page 136: Rails workshop for Java people (September 2015)

Your app should:

4 Do something you want it too

4 We will help.