01 Oct 2014
Items covered:
- Bundle Install/Bundle Exec
- Creating your Rails App
- Folder Directory
- Rake Routes
- 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
- Data View: very much like Excel
- 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)
Create user table
Terminal: rails g migration create_users
Create Users model
Create model - singular, snack_case of Class name
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
- 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
25 Sep 2014
Object Oriented Blackjack Game
- Abstraction - the interaction of objects (keeping track of state and behaviors
- Encapsulation - encapsulates certain behaviors
Steps
- Look at the Requirements
- Pick nouns
- 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.
- 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
22 Sep 2014
Goal
Build a tic-tac-toe game with object oriented programming.
There are 2 Approaches
- Look at Procedural Code and extract classes and objects (more experienced)
- 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
- Draw a board
- Assign Player to "x"
- Assign Computer to "o"
Loop until a winner or all squares are taken
- Player 1 picks an empty square
Check for winner
- Player 2 picks an empty square
- Check for winner
- 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
21 Sep 2014
Items covered:
- Ajax
- Un-obstrusive Javascript
- Jquery
- 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
- Self-managed
- Shared host (no longer popular)
- VPS (most popular)
- AWS, Rackspace, Linode, Digital Ocean
- must have sys admin knowledge
Dedicated Server (hardware) - most expensive
Managed - no sys admin needs, no access to file system
Heroku
Google App Engine
Ninefold
14 Sep 2014
Items covered
- HTTP Request/Response Cycle
- MVC
- Sinatra(Shotgun)
- Erb
- Persistence
Difference b/w websites and web applications
- Websites - static HTML, web server
- Web Applications - Actual server
HTTP
is broken up into 2 actions:
- Request
- HTTP Verbs/Method
- URL
- Parameters
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.
__________________
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
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
main.rb is the game engine.
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.
"set :sessions, true" facilitates some level of persistency, reconstitutes state and allows for your player info to tracked for that session.
Helpers - "helpers do .... end" any methods within this block will be accessible in both the game engine (main.rb) and the (.erb) templates.
Used @error and @success bootstrap instance variables to communicate special messages
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
Keep track of who's turn it is with:
session[:turn] = session[:player_name]
or
session[:turn] = "dealer"
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.
halt erb(:new_player) #means stop, don't execute anything below this, render template instead.
Always include "erb :game" to render the desired template rather than creating a redirect loop.
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>
- You must designate method="post", the default is "get".
- Must give the input "text" an arbitrary name, in this instance, we chose "player_name"
main.rb: under post, params is reset everytime you call it, so we must set the params[:playername] = session[:playername]
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)