/ Coding

Ruby on Rails Notes

Ruby Notes

In-line Conditionals

  • "" will evaluate to true
  • 0 will evaluate to true
  • [] will evaluate to true

Examples

  • fail "Password too short" if password < 8
  • fail "Something ..." unless "Something else"

Short Circuit And

One option:

if user
   if user.signed.in?
      #blahblahblah
   end
end

Another Option

if user && user.signed.in?
     #blahblahblah
   end

Evaluations

result = nill || 1 => returns 1
result = 1 || nil => returns 1 
result = 1 || 2 => returns 1 

Bad Evaluation

 
tweets = timeline.tweets
tweets = [ ] unless tweets

Good Evaluation

tweets = timeline.tweets || []
def sign_in
   current_session || sign user in
end

i = 1
i = || 2
puts i
=> 1

i = 2
i || 2
puts i
=> 2
Bad Code
options[:country] = "us"
  if options[:country].nil?
options[:privacy]
  if options[:privacy].nil?
options[:geotag] = true
  if options[:geotag].nil?
Good Code
options[:country] || = "us"
options[:privacy] || = true
options[:geotag] || = true

###  Conditional Return Values
####  Bad
def list_url(user_name, list_name)
  if list_name
     url = https://twitter.com/#{user_name}/#{list_name}
  else
     url = https://twitter.com/#{user_name}
 end
end

Good

def list_url(user_name, list_name)
  if list_name
    https://twitter.com/#{user_name}/#{list_name}
  else 
    htpps://twitter.com/#{user_name}
  end
end

Case Statements

client_url = case client_name
   when "web"
		  https://twitter.com
   when "Facebook"
      https://twitter.com/Facebook
   else
      nil
  end 

Methods and Classes

Hash Arguments

def tweet(message, options{})
    status = Status.new
    status.lat = options[:lat]
    status.long = options[:long]
    status.body = message
    status.reply_id = options[:reply_id]
    status.post
end
def get_tweets(list)
    if list.authorized?(@user)
       list_tweets
     else
     []
   end
end

Exceptions

def get_tweets(list)
   unless list.authorized?(@user)
      raise AuthorizationException.new
    end
   list.tweets
end
begin
   tweets = get_tweets(my_list)
   rescue AuthorizationException
      warn "You aren't authorized!"
end

Splat Arguments

def mention(status, *names)
   tweets("#{names.join(' '), #{status}")
end

Classes

Arrays

The issue here is that this array contains a name that does not contain a last name so the executed code will return "Madonna, nil"

user_names=[["Ashton", "Kutcher"], ["Wil", "Wheaton"], ["Madonna"]]

user_names.each { |n| puts #{ n[1] }, #{ n{0] } }
class Name
   def initialize(first, last=nil)
      @first = first
      @last = last
   end
   def format
      [@last, @first.compact.join( ' ' )
   end
end

Encapsulation

  • passing data around as numbers breaks encapsulation
  • Places using that data meed to know how to handle it
  • Individual changes require updates to various places
  • wrap in classes

Example:

tweet = tweet.new
tweet.status = "Some String"
tweet.owner_id = current_user_id

send_tweet(tweet)

def send_tweet(message)
   message.owner
      ...
 end

Attribute Accessor

Class Tweet
   attr_accessor ...

  def owner
     retrieve_user(owner_id)
  end

Private methods in Ruby cannot be called with an explicit receiver

Class User
   def up_vote(friend)
      bump_karma
      friend.bump_karma
   end
   protected
   def bump_karma
      puts "Karma up for #{name}"
   end
end

Inheritance

Class Attachment
   attr_accessor :title :size :url
   def to_s
      "#{@title}, #{@size}"
   end
 end

Class Video < Attachment end

Class Image < Attachment end

Specific sub class attr

Class Video < Attachment
    attr_accessor :duration
 end`

Super

Class Grandparent
   def my_method
      "Grandparent: my_method called"
   end
end

Class Parent < Grandparent
end

class Child < Parent
   def my_method
      string= super
   "   #{string}\n my_method called"
   end

Overriding Methods

Class Attachment
   def preview
      case @type
      when :jpg :png :gif
         thumbnail
      when :mp3
         player
      end
   end
end

Overriding ^^

Class Attachment
  def preview
    thumbnail
  end
end

Class Audio < Attachment
   def preview
      player
   end
end

More Instance Variables

Class User
   def display_name
      [@first, @last].join( ' , ' )
   end
   def tweet_handler
      display_name
    end
    def profile
       display_name @description
    end
 end

Activesupport

require 'activesupport/all'

Examples

array [0. 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

array.from(4) => [4. 5. 6. 7. 8. 9. 10]

array.to(2) => [0, 1, 2]

array.in_groups(3) => [ [0, 1, 2], [3.4.5]. [6, 7. 8]. [9. 10, nil] ]

array.split(2) => [ [0. 1], [3, 4, 5, 6, 7, 8, 9, 10] ]

note the integer that the array is split on is removed from the array

my_var = DateTime.new(2014, 12, 14:27:45)
   => "Fri 21, Dec 2014 14:27:45

my_var.at_beginning_of_day
   => Fri Dec 21, 2014 00:00:00

Active Support Extensions

options = {
   lang = 'fr'
   user = 'codeschool'
  }
 
 defaults = {
    lang = 'en'
    country = 'us'
  }

options.reverse_merge(defaults)

=> 
   {
      lang = 'fr'
      user = 'codeschool'
      country = 'us'
   }

Remove Keys

new_option_except(:password)

Error Throw

new_options.assert_valid_keys(:user, :lang)

Alternating Color

def background_class(index)
   return 'white' index.odd?
   return 'grey' index.even?
end

Example:

tweets.each_with_index do | tweet, index |
   puts <div class = "#{background_class}" >
         #{tweets}
        </div>

Modules

Namespace

The following is a file named

image_utils.rb

def preview(image)
end

def transfer(image, destination)
end

The next file is

run.rb

require 'image_utils.rb'

image = user.image
preview(image)

Wrapping in a module

image_utils.rb

module ImageUtils
   def preview(image)
   end
   def transfer(image, destination)
   end
end

run.rb

require 'image_utils'

image = user.image
ImageUtils.preview(image)

Modules in Three Files

image_utils.rb

module ImageUtils
   def preview
   end
   
   def transfer(destination)
   end
end

avatar.rb

require 'image_utils'
   class image
      include ImageUtils
   end

run.rb

image = user.image
image_preview

Ancestors

class Image
  include ImageUtils
end

Image.ancestors [Image, ImageUtils, Object, Kernel BasicObjects]

Image.included_modules[ImageUtils, Kernel]

Rails

CRUD

- Create

- Read

- Update

- Delete

Creating a database

  • Rows
  • Columns
    • First column = integer (id)
      • Second column = string labeled "tweets"
      • third column = string labeled "name"

Hash = multiple keys and values surounded by curly brackets '{ }'

Retrieving a tweet with id of 3

b = { id: 3 }

Hash recipe = variable { key: value }

Anatomy of a hash has keys and values. Keys are also called symbols

Keys Values
:id 3
:status "String Here"
:zombie "Name"

Symbols in Ruby contain ' : ' in front of them.

Extracting part of a hash

b[:status]
  =>"String Here"

Retrieving an object with the id of 3:

t = Tweet.find(3) #This is the method

t now contains the hash for id 3 i.e. :

t = { id: 3, status "String Here", zombie: Name }

Accessing database Tweet is uppercase so that it allows access to lo lowercase, pluralized table names

t = Tweet.find(3)
   puts t[:id}
      => 3
   puts t[:status]
      => "String Here"
   puts t[:zombie]
      => "Name"

Better way is using "dot syntax"

Updating the database

t = Tweet.find(3)
t.zombie
   => "Fucker"
t.save

Now the zombie with :id of 3 is named "Fucker"

Creating a new Tweet

t = Tweet.find(3)
t.status = "Fuck You"
t.save

Now the zombie we named "Fucker" has changed his status to "Fuck You"

To delete a tweet

t = Tweet.find (3)
t.destroy

The recipe for creating items in DB is:

t = TableName.new(hash)
t.save

or

TableName.create(hash)

READ

Tweet.find(2)
   => returns a single tweed with :id of 2
Tweet.find(3, 4, 5)
   => returns an array of tweets with :id 3, 4, 5
Tweet.first
   => returns first tweet
Tweet.last
   => returns last tweet
Tweet.all
   => returns an array with all tweets in the database
Tweet.count
   => total number of tweets

Tweet.order[:zombie]
   => returns all tweets ordered by zombies
Tweet.limit(10)
   => returns first 10 tweets
 Tweet.where(zombie: "ash").order(:status)
    => returns tweets from zombie "Ash" ordered by status

Updating a Table

t = TableName.find(id)
t.key = value
t.save

alternate syntax

t = TableName.find(id)
t.attributes = hash
t.save

alternate syntax

t = TableName.find(id)
t.TableName.update(hash)

Deleting From DB

t = TableName.find(id)
t.destroy

or 

TableName.find(id).destroy

Deleting All

TableName.destroy_all



## **Models**

Models are how the app communicates with the DB in Rails

app/models/tweet.rb


class Tweet < ActiveRecord::Base
end

Capital Tweet anywhere the tweet model is referenced

ActiveRecord::Base maps Tweet model to the tweets <--(plural) table

When Tweet.find(3) is called, an instance of Tweet is returned with the id of 3

Adding Blank Tables

In the tweets.rb model

class Tweet < ActiveRecord::Base
	validates_presence_of :status
end 

For Example

t = Tweet.new
t.save
   =>false
t.error.messages
   => { status: ["Cannot be blank!"] }

Model Validations

Note the keys are just examples

  1. validates_presence_of :status
  2. validates_numerically_of :fingers
  3. validates_uniqueness_of :toothmarks
  4. validates_confirmation_of :password
  5. validates_acceptance_of :zombification
  6. validates_length_of :password, minimum, 3
  7. validates_format_of :email with: /regex/i
  8. validates_inclusion of :age in: 21..99
  9. validates_exclusion of :age in: 0...21, message: "Sorry too young"

Alternate Syntax

validates :status presence: true

validates :status length:{minimum 3}

Relationships

In the tweets table create column called zombie_id

zombie_id is mapped to the id of a zombie in the zombie table

app/models/zombie.rb

class Zombie < ActiveRecord::Base
   has_many :tweets
end

Now a 'tweet' belongs to a zombie

app/models/tweet.rb

class Tweet < ActiveRecord::Base
   belongs_to :zombie
end
ash = Zombie.find(1)
   => { zombie id: 1, name: "ash", graveyard: "hell" }
t.Tweet.create(status: "Fuck you", zombie: ash)
ash.tweets
   =>all tweets from zombie 'ash'
t = Tweet.find(5)
   => returns data for id 5
t.zombie
   =>returns an instance for a zombie
t.zombie.name
   =>"ash"

Views

UI for Rails App

Folder structure

  • zombie_twitter <= App Folder
    • app
      • views
        • zombies
          • index.html.erb
        • tweets
          • index.html.erb
          • show.html.erb

erb = embedded ruby and allows ruby code to be executed within an html document.

<head>...</head>
	<header>...<header>
		<body>
		  <% tweet = Tweet.find(1) %>

<% This is Ruby code %>

<%= This Ruby code is evaluated and printed out %>

DRY = DON'T REPEAT YOURSELF

The "header" and "head" information will be found on every page of the application and should not be copied and pasted into ever view. So to avoid redundancy, all code that is found on every page should be moved into application.html.erb

app/views/layouts/application.html.erb

To show various views in the application.html.erb file, you use

<%= yield %> to render specific views.

<%= link_to text to show, model_instance %>

<%= link_to zombie.name, tweet.zombie, confirm: "Are You Sure?" %>

Creating Layouts

app/views/tweets/index.html.erb

<h1>Listing Tweets</h1>
	<table>
		<tr>
			<th>Status</th>
			<th>Zombie</th>
		</tr>

If we wanted to loop through all tweets for each zombie we would add a loop to the table which would look something like this:

<% Tweet.all.each do |tweet| %>
	<tr>
		<td><%= tweet.status %></td>
		<td><%= tweet.zombie.name %></td>
	</tr>
  <% end %>

**Anywhere in the Rails application 'Tweet refrences a class called a model. Tweet.all is calling the 'all' method on the class 'Tweet'.

Tweet returns an array of tweets.

Calling tweet calls a single tweet from an array.

More Markup

<% tweets = Tweets.all %>
  <% tweets.each do |tweet| %>
  <% end %>
<% if tweets.size == 0 %>
		<em>No Tweets Found</em>
 <% end %>
<% link_to "Edit", edit_tweet_path(tweets) %>
<% link_to "Destroy, tweets.method :delete %>

URL Generators

Action Code URL
List Tweets tweets_path /tweets
New Tweet Form new_tweet_path /tweets/new
Show Tweet tweet /tweets/1
Edit Tweet edit_tweet_path /tweets/1/edit
Delete tweet, method :delete /tweets/1

Controllers

Controllers are the brains that control the models and the views.

app/controllers/tweets_controller.rb

class TweetsController < ApplicationController
   def show
      @tweet = Tweet.find(1)
   end
 end

app/views/tweets/show.html.erb

<h1><%[email protected] %></h1>

The @ symbol turns variables into instance variables

app/controllers/tweets_controller.rb


class TweetsController < ApplicationController

   def show
      @tweet = Tweet.find(1)
      render action: 'status'
   end
end

Params recipe params = { id: 3 }

class TweetsController < ApplicationController
   def show
      @tweet = Tweet.find( params[:id] )
      render action: 'status'
   end
end

More Parameters

/tweets?status=im dead

params = { status: "I'm dead" }

In the controller file:

@tweet = Tweet.create( status: params[:status] )

Sometimes hashes are stored within hashes. Example:

/tweets?tweet[status]=im dead

params = { tweet {status: "I'm dead" } }

in controller file:

decent syntax:

@tweet = Tweet.create(status: params[:tweet][:status]

better syntax:

@tweet = Tweat.create( params[:status] )

Strong Parameters

Specifying the parameter we require:

require(:tweet)

Permit attributes to be set:

permit(:status)

Final code would look like this:

@tweet=Tweet.create(params.require(:tweets).permit(:status))

If multiple things need to be permitted you can use a hash:

permit( [:status, :location] )

Strong parameters only required when creating or updating multiple attributes

Responding with JSON and XML

app/controllers/tweet_controller.rb

class TweetController < ApplicationController
   def show
      @tweet = Tweet.find( params[:id} )
      respond_to do |format|
         format.html # to render html.erb files
         format.json { render json: tweet }
         format.xml { render xml :tweet }
      end
   end
end

Controller Actions

app/controllers/tweet_controller.rb

class TweetController < ApplicationController

   def index    # lists all tweets
   end
   
   def show    # shows a single tweet
   end
   
   def new       # show new tweet form
   end

   def edit      # show edit tweet form
   end
  
  def create    # Create tweet
  end

  def update   # update tweet
  end

  def destroy   # delete a tweet
  end
  

Authentication

app/controllers/tweet_controller.rb

class TweetController < ApplicationController
  def edit
     @tweet = Tweet.find(params[:id])
     if session[zombie_id] != @tweet.zombie
     flash[:notice] = "Stop Bitch"
     redirect_to(tweets_path)
     end
  end
end

app/layouts/application.html.erb


<header>...</header>

...

<% if flash[:notice] %>
	<div id ="notice">
		<%= flash[:notice] %>
	</div>
<% end %>
<%= yield %>

To amend repetition within controller file create method to unify methods that repeat the same code. Example:

app/controllers/tweets_controller.rb


class TweetController < ApplicationController
 
before_action :get_tweet, only: [:edit, :update, :destroy]
before_action :check_auth :only => [:edit, :update, :destroy]

  def get_tweet
     @tweet = Tweet.find(params[:id]
 end

def check_auth
   if session[:zombie_id] != @tweet.zombie
   flash[:notice] = "Stop Bitch"
   redirect to tweets_path
   end
end