Rapid Prototyping with Rails: Lesson 1

Items covered:

  1. Bundle Install/Bundle Exec
  2. Creating your Rails App
  3. Folder Directory
  4. Rake Routes
  5. Relational Databases

Bundle Install

Before you couldn't have multiple versions of gems installed on your machine when switching b/w projects.

Bundler creates a sandbox in your application where the proper versions of your gems are stored.

Bundle exec

does the samething and is used when your system level rake (rake db:migrate) for example is not compatible with the current settings. Therefore you have to use bundle exec rake db:migrate or just bundle exec.

Heroku also uses Bundler.

Creating your app:

Files you should always look at:

1. database.yml

Tells the application how to connect to the database *In development - we use sqlite3 *In production - we use postgres (although in this course Heroku takes care it for us)

For sqlite - your database is in your code base and its one file, unlike others where is an application and usually your database is hosted somewhere:

production
        adapter: mysql
        database: myapp_development
        host: localhost
        username:
        password:

You will want to mimick your local enviornment as much as possible to your production enviornment in order to catch bugs

2. Gemfile

3. Routes File

How the request initally get's routed. The blueprint of your application ad its capabilities.

Folder Directory

app - where you spend most time
  assets - where you put all static files, images, etc.
  controllers
  mailers - outbound mail. Not good to use because you can easily get flagged as spam
   (use a transactional email provider (stmp mailers) hosts such as mailgun, postmark, etc. )
   models
   views - where our erb goes
bin - where your executable code goes - we never touch this
application.rb - sets application defaults such as timezone, load path
enviornment folder - configurations for development and production, and testing.
                  - you can also create custom enviornments - such QA, intergration or staging
initializers folder - where you files that you want to load when your application
                    is initialized
Config folder
  locales - yml files - is a way to internationalize your app. Is comprised of comlicated hashes
        and you no longer write using strings. Extremely painful to manage files
Lib - you can also put assets in there
Tasks - you can place rake tasks in here
log - stores all terminal logs from your application
public - where you store your error pages
      - robosts - tell search engines where to go within your application
Vendor - a relic from the past
.gitignore - tells git what files to ignore when committing. (.files are hidden files)
Readme - is what is dispayed when you push your app to Github.

Rake Routes

shows all possible endpoints in your application You can also view your routes in your browser by going to localhost:3000/rails/info/routes in Rails 4 only or you can use the terminal: rake routes | grep _______

Relational Databases:

A series of spreadsheets.

To connect these tables in order to link infromation: 1. primarykey in rails is the "id" 2. foreignkey is the creation of a "userid" column with the primarykey in on other tables to connect to the main table.

There are 2 Views

  1. Data View: very much like Excel
  2. Schema View: lists the column name and the data type allowed for that column.

1:M

the Foreign_key is always on the many side of a one to many relationship

ORMs

ActiveRecord is an ORM (Object Relational Mapper), a pattern created by Martin Fowler which describes how to abstract data from a database into code. Prior to this, using PHP, you would write sql code directly in the views.

In console, using "= _" is equivalent to your last returned item.

Rails is good for simple, applications with quick builds that aren't for existing application is analytics.

Setting up 1:M b/w posts and users (Posts were previously created)

  1. Create user table Terminal: rails g migration create_users

  2. Create Users model Create model - singular, snack_case of Class name

  3. Need to add foreign key to the many side (the Posts table) Terminal: rails g migration adduseridtoposts

Inside migration file:

def change
    add_column :table_name :foreign_key (which is normally the resource+primary_key), :datatype (always will be integer)

    add_column :posts, :user_id, :integer
  end
  1. Write in hasmany and belongsto on the models

Virtual Attribute

attributes that are not columns, are given through associations

Mass Assignment

The ability create/update AR obj through a hash where the keys match the attirbute or virtual attribute. example:

Post.new(title: "some title", url: "some url", user = User.first)

Post.new & Post.create

Both can be used for mass assignment. Post.new is in memory (no user id assigned) while Post.create will update to the database immediately.

Post.build and Post.new are the same.

When in the console:

You can run the following commands:
  user = User.first
  post = Post.first
  post.user **** virtual attribute
  post.title
  post.user_id = user ***NO, you can only set objects to object and integers to integers
  post.user_id = user_id

Intro to Ruby & Web Dev: Blackjack OO Game

Object Oriented Blackjack Game

  1. Abstraction - the interaction of objects (keeping track of state and behaviors
  2. Encapsulation - encapsulates certain behaviors

Steps

  1. Look at the Requirements
  2. Pick nouns
  3. Think off all actions you want the object to perform and write down method calls (i.e. player.deal) as though the methods already exists.
  4. Do this for each class.
class Card
  attr_accessor :suit, :face_value

  def initialize(s,fv)
    @suit = s
    @face_value = fv
  end

  def pretty_output
    "The #{face_value} of #{find_suit}"
  end

  def to_s
    pretty_output
  end

  def find_suit
    ret_val = case suit
                when 'H' then 'Hearts'
                when 'D' then 'Diamonds'
                when 'S' then 'Spades'
                when 'C' then 'Clubs'
              end
    ret_val
  end
end

class Deck
  attr_accessor :cards

  def initialize
    @cards = []
    ['H', 'D', 'S', 'C'].each do |suit|
      ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'].each do |face_value|
        @cards << Card.new(suit, face_value)
      end
    end
    scramble!
  end

  def scramble!
    cards.shuffle!
  end

  def deal_one
    cards.pop
  end

  def size
    cards.size
  end
end

module Hand
  def show_hand
    puts "---- #{name}'s Hand ----"
    cards.each do|card|
      puts "=> #{card}"
    end
    puts "=> Total: #{total}"
  end

  def total
    face_values = cards.map{|card| card.face_value }

    total = 0
    face_values.each do |val|
      if val == "A"
        total += 11
      else
        total += (val.to_i == 0 ? 10 : val.to_i)
      end
    end

    #correct for Aces
    face_values.select{|val| val == "A"}.count.times do
      break if total <= 21
      total -= 10
    end

    total
  end

  def add_card(new_card)
    cards << new_card
  end

  def is_busted?
    total > Blackjack::BLACKJACK_AMOUNT
  end
end

class Player
  include Hand

  attr_accessor :name, :cards

  def initialize(n)
    @name = n
    @cards = []
  end

  def show_flop
    show_hand
  end

end

class Dealer
  include Hand

  attr_accessor :name, :cards

  def initialize
    @name = "Dealer"
    @cards = []
  end

  def show_flop
    puts "---- Dealer's Hand ----"
    puts "=> First card is hidden"
    puts "=> Second card is #{cards[1]}"
  end
end

class Blackjack
  attr_accessor :deck, :player, :dealer

  BLACKJACK_AMOUNT = 21
  DEALER_HIT_MIN = 17

  def initialize
    @deck = Deck.new
    @player = Player.new("Player1")
    @dealer = Dealer.new
  end

  def set_player_name
    puts "What's your name?"
    player.name = gets.chomp
  end

  def deal_cards
    player.add_card(deck.deal_one)
    dealer.add_card(deck.deal_one)
    player.add_card(deck.deal_one)
    dealer.add_card(deck.deal_one)
  end

  def show_flop
    player.show_flop
    dealer.show_flop
  end

  def blackjack_or_bust?(player_or_dealer)
    if player_or_dealer.total == BLACKJACK_AMOUNT
      if player_or_dealer.is_a?(Dealer)
        puts "Sorry, dealer hit blackjack. #{player.name} loses."
      else
        puts "Congratulations, you hit blackjack! #{player.name} win!"
      end
      play_again?
    elsif player_or_dealer.is_busted?
      if player_or_dealer.is_a?(Dealer)
        puts "Congratulations, dealer busted. #{player.name} win!"
      else
        puts "Sorry, #{player.name} busted. #{player.name} loses."
      end
      play_again?
    end
  end

  def player_turn
    puts "#{player.name}'s turn."

    blackjack_or_bust?(player)

    while !player.is_busted?
      puts "What would you like to do? 1) hit 2) stay"
      response = gets.chomp

      if !['1', '2'].include?(response)
        puts "Error: you must enter 1 or 2"
        next
      end

      if response == '2'
        puts "#{player.name} chose to stay."
        break
      end

      #hit
      new_card = deck.deal_one
      puts "Dealing card to #{player.name}: #{new_card}"
      player.add_card(new_card)
      puts "#{player.name}'s total is now: #{player.total}"

      blackjack_or_bust?(player)
    end
    puts "#{player.name} stays at #{player.total}."
  end

  def dealer_turn
    puts "Dealer's turn."

    blackjack_or_bust?(dealer)
    while dealer.total < DEALER_HIT_MIN
      new_card = deck.deal_one
      puts "Dealing card to dealer: #{new_card}"
      dealer.add_card(new_card)
      puts "Dealer total is now: #{dealer.total}"

      blackjack_or_bust?(dealer)
    end
    puts "Dealer stays at #{dealer.total}."
  end

  def who_won?
    if player.total > dealer.total
      puts "Congratulations, #{player.name} wins!"
    elsif player.total < dealer.total
      puts "Sorry, #{player.name} loses."
    else
      puts "It's a tie!"
    end
    play_again?
  end

  def play_again?
    puts ""
    puts "Would you like to play again? 1) yes 2) no, exit"
    if gets.chomp == '1'
      puts "Starting new game..."
      puts ""
      deck = Deck.new
      player.cards = []
      dealer.cards = []
      start
    else
      puts "Goodbye!"
      exit
    end
  end

  def start
    set_player_name
    deal_cards
    show_flop
    player_turn
    dealer_turn
    who_won?
  end
end

game = Blackjack.new
game.start

Intro to Ruby & Web Dev: Tic-Tac-Toe OO Game

Goal

Build a tic-tac-toe game with object oriented programming.

There are 2 Approaches

  1. Look at Procedural Code and extract classes and objects (more experienced)
  2. Write out psuedo code (put rules into words describing the game, list nouns and common verbs

Start with an empty 3x3 board.

Board with two players One player is "x", the other is "o", and player "x" goes first. Then they alternate until 3 of x/o in a row or all cells are ful, which means its a tie.

Extract Classes

Board

  • squares
  • Are all squares marked?
  • Find all empty squares ###Player
  • name
  • marker

Square

you wouldn't want to say square is aware of position, b/c the square is no longer reusable - occupied? - mark(marker)

Game_engine

is the procedural peice to the game that will orchestrate the game Sequence of steps

  1. Draw a board
  2. Assign Player to "x"
  3. Assign Computer to "o" Loop until a winner or all squares are taken
  4. Player 1 picks an empty square Check for winner
  5. Player 2 picks an empty square
  6. Check for winner
  7. If there is a winner, show winner; else "It's a tie"
class Board
  WINNING_LINES = [[1,2,3], [4,5,6], [7,8,9], [1,4,7], [2,5,8], [3,6,9], [1,5,9], [3,5,7]]

  def initialize
    @data = {}
    (1..9).each {|position| @data[position] = Square.new(' ') }
  end

  def empty_squares
    @data.select {|_, square| square.empty?}.values
  end

  def empty_positions
    @data.select {|_, square| square.empty?}.keys
  end

  def all_squares_marked?
    empty_squares.size == 0
  end

  def three_squares_in_a_row?(marker)
    WINNING_LINES.each do |line|
      return true if @data[line[0]].value == marker && @data[line[1]].value  == marker && @data[line[2]].value == marker
    end
    false
  end

  def mark_square(position, marker)
    @data[position].mark(marker)
  end

  def draw
    system 'clear'
    puts
    puts "     |     |"
    puts "  #{@data[1]}  |  #{@data[2]}  |  #{@data[3]}"
    puts "     |     |"
    puts "-----+-----+-----"
    puts "     |     |"
    puts "  #{@data[4]}  |  #{@data[5]}  |  #{@data[6]}"
    puts "     |     |"
    puts "-----+-----+-----"
    puts "     |     |"
    puts "  #{@data[7]}  |  #{@data[8]}  |  #{@data[9]}"
    puts "     |     |"
    puts
  end
end

class Player
  attr_reader :marker, :name
  def initialize(name, marker)
    @name = name
    @marker = marker
  end
end

class Square
  attr_reader :value

  def initialize(value)
    @value = value
  end

  def empty?
    @value == ' '
  end

  def mark(marker)
    @value = marker
  end

  def to_s
    @value
  end
end

class Game
  def initialize
    @board = Board.new
    @human = Player.new("James", "X")
    @computer = Player.new("R2D2", "O")
    @current_player = @human
  end

  def current_player_marks_square
    if @current_player == @human
      begin
        puts "Choose a position #{@board.empty_positions} to place a piece:"
        position = gets.chomp.to_i
      end until @board.empty_positions.include?(position)
    else
      position = @board.empty_positions.sample
    end
    @board.mark_square(position, @current_player.marker)
  end

  def alternate_player
    if @current_player == @human
      @current_player = @computer
    else
      @current_player = @human
    end
  end

  def current_player_wins?
    @board.three_squares_in_a_row?(@current_player.marker)
  end

  def play
    @board.draw
    loop do
      current_player_marks_square
      @board.draw
      if current_player_wins?
        puts "The winner is #{@current_player.name}!"
        break
      elsif @board.all_squares_marked?
        puts "It's a tie."
        break
      else
        alternate_player
      end
    end
    puts "Bye"
  end
end

Game.new.play

Intro to Ruby & Web Dev: Lesson 3

Items covered:

  1. Ajax
  2. Un-obstrusive Javascript
  3. Jquery
  4. Deployment Options

Ajax

is used in a scernario when elements on the page are changing but you only want thos elements to change rather reload (reconstituting the state of your application) each time. Pages can be computationally expensive and create a bad user interface. Ajax will issue the request behind the scences and the when the response comes back, your Javascript code must handle that data. This is using unobstrusive Javascript. For example, if you have likes on your Facebook page, we will want to see an increment in the number of likes and the like button go from "like" to "unlike". However, for our use case, only the URL won't change.

Un-obsturive Javascript

Back in the day, Js used to be added directly to the HTML but now a js file is created just like the stylesheet.css file added to the layout.erb JQuery is now the defacto Js Library.

You must add the <script src="/application.js"></script> to the layout.erb
Google hosts Jquery for you!
1.x snippet: <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
  • # is an anchor tag ##JQuery .class - same style #id - unique element on your DOM
$(document).ready(funtion(){
    $("element").click(funtion() {
        $(#player)_area).css('background-color', 'yellow')
        return false;===> do not re-execute
      });
  });

un-obstrusive Js allows you to hijack the webpage. You shouldn't create elaborate pages with a bunch of event-listeners. Using JQuery in this manner can be hard to manage. So if you're looking to create an enriched user-experience, you should use Angular, Backbone, and/or Ember. So more like dashes and sprinkles of Jquery rather than overload of JQuery

Hijacking (Adding JQuery to) the "hit-button"

$(document).ready(function() {
  $('#hit_form input').click(function() {
    alert('hit button clicked!');
    return false;
    });
  });

use console.log (to inspect/debug in Firefox)

Using Ajax

$(document).ready(function() {
  #$('#hit_form input').click(function() { #must be rebind with the input again after indcating layout false
    $document).on(click, '#hit_form input', function()) {

    $.ajax({ #this is a hash, much include comma after entry
      type: 'POST',
      url: '/game/player/hit'
      #data: #also known as parameters
      )}.done(function(msg) {
        #$('#game').html(msg); #create div for game. Can't use HTML b/c its creating a duplicate game div id
        $('#game').replaceWith(msg);
        });
      });

    return false;
    });
  });

Deployment Options

  1. Self-managed
  2. Shared host (no longer popular)
  3. VPS (most popular)
    • AWS, Rackspace, Linode, Digital Ocean
    • must have sys admin knowledge
  4. Dedicated Server (hardware) - most expensive

  5. Managed - no sys admin needs, no access to file system

  6. Heroku

  7. Google App Engine

  8. Ninefold

Intro to Ruby & Web Dev: Lesson 3

Items covered

  1. HTTP Request/Response Cycle
  2. MVC
  3. Sinatra(Shotgun)
  4. Erb
  5. Persistence

Difference b/w websites and web applications

  1. Websites - static HTML, web server
  2. Web Applications - Actual server

HTTP

is broken up into 2 actions:

  1. Request
    • HTTP Verbs/Method
    • URL
    • Parameters
  2. Response

    • Status Code
    • Payload (not necessarily HTML all the time... could be JSON, XML) HTTP is also stateless. B/w every request, the browser must reconsitute the world to resemeble that current state. __________________
  3. Request - any interaction that you have with a web application

    • 2 Types of Requests - this is how a user interacts with the website
    • HTTP Verbs
    • GET
      • Link or type into address bar
      • smaller payload (there is a size limit)
      • parameters are a apart of the URL
    • POST
      • forms
      • larger payload
      • parameters are submitted in the body
    • parameters are submitted as apart of a form
    • HTTP Methods
  4. Response

    • Status Code
    • 200: ok
    • 301: redirect
    • 404: File not found
    • 500: general error

MVC

Person visits site and a request is to the controller/actions. Depending on the HTTP Verb, URL, and parameters passed - the controller will reference the model for the logic and the model, in turn will potentially reach to the database. Once retrieved, the controller render a view.

We are foregoing the model and focusing on the request and render features, using a cookie as our sessions.

Sinatra App

Sinatra uses localhost:4567

Sinatra doesn't automatically reload - for this, install shotgun and run 'ruby main.rb' => localhost:9393

Shotgun

Allows your browser to refresh and updates with changes without stopping the server.

Install in terminal: gem install Shotgun.

Launched via port:9393 by running "shotgun main.rb"

Link is loaded at '/'

In main.rb:

get '/' do
  "Hello World" +params[:some].to_s
end

Erb

Instead of working with code directly - HTML, etc , we want to work with templates (.erb)

  get 'test/' do
    erb :test
  end
  • Create test.erb (layout: false) - to remove layout

Instance variables setup in the action are available in the template, but only the template is being rendered

Erb => Embedded Ruby

<% %> Ruby code that doesn't need to be printed to HTML <%= %> Ruby code that should be printed out to HTML


main.rb
get '/test' do
  @error = "We've got a problem"
  erb :test *** points to the test.erb template under the views***
end

Persistance

The problem is that accessing the instance variable is only available upon request. To display form:

#main.rb
get '/' do
  erb :set_name
end
#set_name.erb (Handles the display of data)
<h4> Set Player Name </h4>

<form action='/set_name' method='post'
  Name: <input type='text' name='player_name' />
  </br>
  <input type ='submit' class='btn'/>
</form>
#main.rb (handle the submission of the data)

post 'set_name' do
end

Whenever making changes to the HTML, you must refresh the browser to take effect.

In order to fix persistence problem, you must use session instead of an instance variable. This allows use to work with cookies( has a limit of 4 kilobytes instead of dealing with relational databases. Can't deal with more than one decks.:

post '/set_name' do
  session[:player_name] = params[:player_name]
end

Sessions can be referenced directly because of the use of cookies. We know that cookies have tied to the session because we did not reference an instance variables.

You can use the following to reference in your templates in order to display content dynamically and reconstitute all the states you expect. 1. Instance variables 2. Sessions 3. helper methods

The key is to figure out when you will redirect or render:

  • Post - typically redirect
  • Get - typically render

Sinatra is typically very much to the metal and handles much less than Rails.

NOTES FROM BLACKJACK GAME

  1. main.rb is the game engine.

  2. Constants: use constants when numbers are consistent through out the the game. This will allow you to be able the rules of the game in the future without having to comb through the code to find where to make the change.

  3. "set :sessions, true" facilitates some level of persistency, reconstitutes state and allows for your player info to tracked for that session.

  4. Helpers - "helpers do .... end" any methods within this block will be accessible in both the game engine (main.rb) and the (.erb) templates.

  5. Used @error and @success bootstrap instance variables to communicate special messages

  6. Require new player to enter in name: post '/newplayer' do if params[:playername].empty? @error = "Name is required." halt erb(:newplayer) #stop, don't execute anything below this, render template instead. end session[:playername] = params[:player_name] redirect '/game' end

  7. Keep track of who's turn it is with:

session[:turn] = session[:player_name]
      or
    session[:turn] = "dealer"
  1. Instance Variables: using instance variables allow your game engine to communicate to your layout template and the data is erased as soon as another request comes in.

  2. halt erb(:new_player) #means stop, don't execute anything below this, render template instead.

  3. Always include "erb :game" to render the desired template rather than creating a redirect loop.

  4. When using post in main.rb and forms in .erb, you must specify the method as "post" in erb becasue the default is GET if you don't specify.

<form action='/new_player' method="post">
      <input type="text" name="player_name" />
      <br/>
      <input type="submit" class="btn btn-inverse" value="Let's Play!"/>
    </form>
  1. You must designate method="post", the default is "get".
  2. Must give the input "text" an arbitrary name, in this instance, we chose "player_name"
  3. main.rb: under post, params is reset everytime you call it, so we must set the params[:playername] = session[:playername]

  4. There are different input types for forms including:

input type = submit input type = text, name = :player_name (must specify a name to track the value)