Skip to content

Instantly share code, notes, and snippets.

@gbanis
Last active August 29, 2015 14:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gbanis/4729d626c53ce20256c3 to your computer and use it in GitHub Desktop.
Save gbanis/4729d626c53ce20256c3 to your computer and use it in GitHub Desktop.
=begin
LEVEL 1: DEEP IN THE CRUD
====================================================================
CRUD = Create, Read, Update, Delete
CREATE
---------------------------------------------------------
syntax: Recipe:
--------- ---------
t = tweet.new t = TableName.new
t.status = "I <3 brains." t.key = value
t.save t.save
Alt syntax: Recipe:
--------- ---------
t = Tweet.new( t = TableName.new(hash)
status: "I <3 brains", t.save
zombie: "Jim")
t.save
Alt syntax: Recipe:
--------- ---------
Tweet.create(status: "I <3 brains",zombie: "Jim") TableName.create(hash)
READ
--------------------------------------------------------
Tween.find(2,3,4,5) => returns tweets at indeces 2,3,4,5
Tweet.first => returns first tweet
Tweet.last => returns last tweet
Tweet.all => returns all tweets
Tweet.count => returns total number of tweets
Tweet.order => returns tweets, ordered by zombies
Tweet.limit(10) => returns the first 10 tweets
Tweet.where(zombie: "ash") => returns all tweets from zombie named 'ash'
Many of these methods can be chained together to create a complex query
Tweet.where(zombie: 'ash').order(:status).limit(10)
=> returns
only tweets from zombie 'ash'
ordered by status
only the first 10
UPDATE
------------------------------------------------------------
syntax Recipe
---------- ---------------------
t = Tweet.find(2) t = TableName.find(id)
t.zombie = "EyeballChomper" t.key = value
t.save t.save
alt syntax Recipe
---------- ----------
t = Tweet.find(2) t = TableName.find(id)
t.attributes = { t.attributes = hash
status: "Can I munch your eyeballs?", t.save
zombie: "EyeballChomper"
}
t.save
alt syntax Recipe
------------- -------------
t = Tweet.find(2) t = Tweet.find(2)
t.update( t = TableName.update(hash)
status: "Can I munch your eyeballs?",
zombie: "EyeballChomper"
)
DELETE
------------------------------------------------------------------
syntax recipe
--------- ---------
t = Tweet.find(2) t = Table.find(id)
t.destroy t.destroy
alt syntax
-----------
t = Tweet.find(2).destroy TableName.find(id).destroy
alt syntax
-----------
Tweet.destroy_all TableName.destroy_all
=> destroys all the tweets
=end
# =====================================================================
# /////////////////////////////////////////////////////////////////////
# =====================================================================
=begin
LEVEL 2: MODELS- LIFEBLOOD OF YOUR APPLICATION
====================================================================
Models are how you communicate with your data storage in rails
Application stack:
Models
app/models/tweet.rb:
class Tweet < ActiveRecord::Base
end
t = Tweet.find(3)
t is an instance variable of the class
if we wanted to make sure that status was never blank, we
go back into our class and validate :status
class Tweet < ActiveRecord::Base
validates_presence_of :status
end
>> t = Tweet.new
=> #<Tweet id: nil, status: nil, zombie: nil>
>> t.save
=> false
>> t.errors.messages
=> {status: ["can't be blank"]}
>> t.errors[:status][0]
Rails comes with a slew of validation methods out of the box:
----------------------------------------------------------------
validates_presence_of :status
validates_numericality_of :fingers
validates_uniqueness_of :toothmarks
validates_confirmation_of :password
validates_acceptance_of :zombiefication
validates_length_of :password, minimum: 3
validates_format_of :email, with: /regex/i
validates_inclusion_of :age, in: 21..99
validates_exclusion_of :age, in: 0..21, message: "sorry, you must be over 21"
syntax
---------------
validates :status, presence: true
validates :status, length: {minimum: 3}
alt syntax
----------------
validates :status,
presence: true,
length: { minimum: 3 }
syntax for additional options:
--------------------------------
presence: true,
uniqueness: true
numericality: true
length: { minimum: 0, maximum: 2000 }
format: { with: /.*/ }
acceptance: true
confirmation: true
RELATIONSHIPS:
----------------------------------------
Relationships exist between different tables, we need
a way to tell our application how to make relationships
"A zombie has this many tweets"
class Zombie < ActiveRecord::Base
has_many :tweets #=> lowercase, plural of the table you are referencing
end
"A tweet belongs to:"
class Tweet < ActiveRecord::Base
belongs_to :zombie #=> singular, because a tweet can only belong to 1 zombie
end
ash = Zombie.find(1)
=> #<Zombie id: 1, name: "Ash", graveyard: "Glen Haven Memorial Cemetary">
t = Tweet.create(
status: "Your eyelids taste like bacon.",
zombie: ash
)
=> #<Tweet id: 5, status: "Your eyebalss taste like bacon.", zombie_id: 1>
ash.tweets.count
=> 3
ash.tweets
=> [array of 3 instances that belong to ash]
if we fetched this tweet
------------------
t = Tweet.find(5)
=> #<Tweet id: 5, status: "Your eyelids taste like bacon.", zombie_id: 1>
then we could call:
------------------
t.zombie
=> #<Zombie id: 1, name: "Ash", graveyard: "Glen Haven Memorial Cemetary"> (instance of the zombie)
we could even call
------------------
t.zombie.name
=> "Ash"
=end
# =====================================================================
# /////////////////////////////////////////////////////////////////////
# =====================================================================
=begin
LEVEL 3: THE VIEWS AIN'T ALWAYS PRETTY
=======================================================================
Views are where we find our user interface. Views are the front end. The visual representation of our app.
Assuming the folder structure of our application looks like the following:
Zombie_twitter
app
views
zombies
tweets
index.html.erb (list all tweets)
show.html.erb (view a tweet)
.erb: Embedded Ruby
show.html.erb:
<!DOCTYPE html>
<html>
<head></head>
<body>
<header>...</header>
<!-- <% yield %> yield says "this is where the contents of this particular page goes-->
<!-- the following lines get moved into an external file -->
<% tweet = Tweet.find(1) %>
<h1><%= tweet.status %></h1>
<p><%= tweet.zombie.name %></p>
</body>
</html>
The above code is moist. We need to keep our code DRY: Don't repeat yourself
We can take this embedded code and move it out into /app/views/layouts/application.html.erb
syntax Creating a link inside .erb file:
-----------------------------------------
<%= link_to tweet.zombie.name, zombie_path(tweet.zombie) %>
should never have to hard code urls, rails comes baked in with helper functions.
alt syntax
-----------------
<%= link_to tweet.zombie.name, tweet.zombie %>
Link Recipe:
-------------------
<%= link_to text_to_show, model_instance %>
Looking up Documentation:
---------------------------------------
1. Rails sourcecode
2. Documentation that's already been generated
api.rubyonrails.org
To use the confirm data attribute:
<%= link_to tweet.zombie.name,
tweet.zombie,
confirm: "Are you sure?" %>
Inside /app/views/tweets/index.html.erb
--------------------------------------------------------------------------
<h1>Listing Tweets</h1>
<table>
<tr>
<th>Status</th>
<th>Zombie</th>
</tr>
<% Tweet.all.each do |tweet| %>
<tr>
<td><%= tweet.status %></td>
<td><%= tweet.zombie.name %></td>
</tr>
<% end %>
</table>
Tweet class
Tweet.all array of tweets
tweet single tweet
To create links
----------------------------------------------------------------------
<h1>Listing Tweets</h1>
<table>
<tr>
<th>Status</th>
<th>Zombie</th>
</tr>
<% Tweet.all.each do |tweet| %>
<tr>
<td><%= link_to tweet.status, tweet %></td>
<td><%= link_to tweet.zombie.name, tweet.zombie %></td>
</tr>
<% end %>
</table>
What if we don't have any tweets yet?
-------------------------------------------------------------------------------
<% Tweet.all.each do |tweet| %>
<tr>
<td><%= link_to tweet.status, tweet %></td>
<td><%= link_to tweet.zombie.name, tweet.zombie %></td>
</tr>
<% end %>
---------------turns into:------------------
<% tweets = Tweet.all %> # => we assign the tweets into a variable.
<% tweets.each do |tweet| %> # => then we loop through that variable
<tr>
<td><%= link_to tweet.status, tweet %></td>
<td><%= link_to tweet.zombie.name, tweet.zombie %></td>
</tr>
<% end %>
<% if tweets.size == 0 %> # => conditional, if tweets don't exist, print out the message
<em>No Tweets Found</em>
<% end %>
Edit and Delete Links
--------------------------------------------------------------------------------
<% tweets.each do |tweet| %> # => then we loop through that variable
<tr>
<td><%= link_to tweet.status, tweet %></td>
<td><%= link_to tweet.zombie.name, tweet.zombie %></td>
<td><%= link_to "Edit", edit_tweet_path(tweet) %></td>
<td><%= link_to "Destroy", tweet, method: :delete %></td>
</tr>
<% end %>
URL Generators
------------------------------------------------------------
Action Code The URL Notes
--------------------------------------------------------------------------------------------
list all tweets tweets_path /tweets
new tweet form new_tweet_path /tweets/new
-------------tweet = Tweet.find(1) *****need a path for these*****------------------------------
Show a tweet tweet /tweets/1
Edit a tweet (tweet) /tweets/1/edit
Delete a tweet tweet, method: :delete /tweets/1
Link Recipe:
---------------
<%= link_to text_to_show, code %>
=end
# =====================================================================
# /////////////////////////////////////////////////////////////////////
# =====================================================================
=begin
LEVEL 4: controllers
=======================================================================
Controllers: the brains of the application
Typically where you use models to get data out of the database, and
you use views to display the data that comes out of the models.
Application Stack:
Views
Models
Controllers
Really shouldn't be called the model in the views. Before a request goes to our model, the request
will go to a controller first.
=end
# Show a tweet
# ——————————————————————————————————————————————————
# /app/controllers/tweets_controller.rb
# --------------------------------------------
class TweetsController < ApplicationController
def show
@tweet = Tweet.find(1)
end
end
# /app/views/tweets/show.html.erb
# --------------------------------------------
<h1><%= @tweet.status %></h1>
<p>Posted by <%= @tweet.zombie.name %></p>
# Notes
# ----------------------------
=begin
no coincidence that the show method is named the same as the view.
this is typically where we call our models. Let's fix it!
What about variable scope? We need to add the "@" symbol in front of the instance variables.
Grant views access to variables with "@"
=end
# Rendering a Different View
# ———————————————————————————————————————————————————
# /app/controllers/tweets_controller.rb:
# ----------------------------------------
class TweetsController < ApplicationController
def show
@tweet = Tweet.find(1)
render action: 'status'
end
end
# /app/views/tweets/status.html.erb
# ---------------------------------
<h1><%= @tweet.status %></h1>
<p>Posted by <%= @tweet.zombie.name %></p>
# Accepting Parameters
# ——————————————————————————————————————————————————
class TweetsController < ApplicationController
def show
@tweet = Tweet.find(params[:id])
render action: 'status'
end
end
# if we wanted to go to different urls and specify the id of the tweets?
# Rails generates a hash of paramters when we use the link_to
# Params Recipe:
# ---------------
params = { id: "1" }
# Parameters
# ——————————————————————————————————————————————————
# /tweets?status=Imdead
params = { status: "I'm dead" }
@tweet = Tweet.create(status:params[:status])
# /tweets?tweet[status]=Imdead
params = { tweet: {status: "I'm dead"} } #hash within a hash
@tweet = Tweet.create(status: params[:tweet][:status])
# alt syntax
# ------------
@tweet = Tweet.create(params[:tweet])
# There's something rotten about this code. In Rails 4 we're required to use strong parameters.
# We need to specify the parameter key we require
require(:tweet)
# We also need to specify the attributes we will permit to be set.
permit(:status)
# **************
@tweet = Tweet.create(params.require[:tweet].permit(:status))
# **************
# if there were multiple things we needed to permit, we pass in an array, like so:
params.require(:tweet).permit([:status, :location])
@tweet = Tweet.create(params[:tweet])
# Strong params required only when:
# Creating or Updating with Multiple Attributes
# Respond with XML or JSON
# ——————————————————————————————————————————————————
# Rails is great for a backend API
# for JSON representation of our tweet:
# -------------------------------------
# /tweets/1.json
# /app/controllers/tweets_controller.rb
# --------------------------------------
class TweetsController < ApplicationController
def show
@tweet = Tweet.find(params[:id])
respond_to do |format|
format.html #show.html.erb
format.json { render json: @tweet }
end
end
# JSON
# --------
{tweet:
{
"id" : 1,
"status": "Where can I get a good bite to eat?",
"zombie_id": 1
}
}
# for XML Representation of our tweet:
# --------------------------------------
# /tweets/1.xml
# /app/controllers/tweets_controller.rb
# --------------------------------------
class TweetsController < ApplicationController
def show
@tweet = Tweet.find(params[:id])
respond_to do |format|
format.html #show.html.erb
format.json { render json: @tweet }
format.xml { render xml: @tweet }
end
end
# xml:
<?xml version="1.0" encoding="UTF-8">....
# Controller Actions
# ——————————————————————————————————————————————————
class TweetsController < ApplicationController
def index List all tweets
def show Show a single tweet
def new show a new tweet form
def edit Show an edit tweet form
def create show an edit tweet form
def update update a tweet
def destroy delete a tweet
end
# Most of the actions will have views associated with them.
# Adding Some Authentication
# ——————————————————————————————————————————————————
# We need to add some sort of simple authentication.
# /app/controllers/tweets_controller.rb
# -----------------
class TweetsController < ApplicationController
def edit
@tweet = Tweet.find(params[:id])
if session[:zombie_id] != @tweet.zombie_id
flash[:notice] = "Sorry, you can't edit this tweet"
redirect_to(tweets_path)
end
end
end
# session: Works like a per user hash
# flash[:notice]: To send messages to the user
# redirect_to(params): allows you to redirect the browser
# alt syntax
# ----------
redirect_to(tweets_path,
notice: "Sorry, you can't edit this tweet")
# Notice for layouts
# ------------------
<html>...
<body>...
<% if flash[:notice] %>
<div id="notice"><%= flash[:notice] %></div>
<% end %>
<%= yield %>
# /app/controllers/tweets_controller.rb
# -------------------------------------
class TweetsController < 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_id
flash[:notice] = "Sorry, you can't edit this tweet"
redirect_to(tweets_path)
end
end
def edit
def update
def destroy
end
#
#
#
#
# =====================================================================
# /////////////////////////////////////////////////////////////////////
# =====================================================================
#
#
#
#
#
=begin
LEVEL 5: Routes
=======================================================================
Application Stack:
Views
Models
Controllers
Routes
Routing through Rails: In order to properly find these paths, and route them into actions, we need to define routes inside our router.
We need to define routes.
Inside:
zombie_twitter
config
routes.rb
=end
# routes.rb
ZombieTwitter::Application.routes.draw.do
resources :tweets #restful route
end
=begin
to create a custom route inside our router, we have to specify what method to accept
=end
ZombieTwitter::Application.routes.draw.do
resources :tweets #restful route
get '/new_tweet' => 'tweets#new'
# path controller#action
end
=begin Named Routes
=end
ZombieTwitter::Application.routes.draw.do
resources :tweets #restful route
get '/new_tweet' => 'tweets#new'
get '/all' => 'tweets#index'
end
# what if we want to link to this custom path?
# <%= link_to "All Tweets", "" %>
ZombieTwitter::Application.routes.draw.do
resources :tweets #restful route
get '/new_tweet' => 'tweets#new'
get '/all' => 'tweets#index', as: 'all_tweets'
end
# <%= link_to "All Tweets", all_tweets_path %>
# Redirect /all to /tweets
get '/all' => redirect('/tweets')
# Root Route
# the root domain of your site/application
root to: "tweets#index"
# Linking to root
# <%= link_to "All Tweets", root_path %>
# Route parameters
# what is someone wanted to enter in a zip code and only see tweets in their local area?
# We already have a tweets controller:
# /app/controllers/tweets_controller.rb
def index
if params[:zipcode]
@tweets = Tweet.where(zipcode: params[:zipcode])
else
@tweets = Tweet.all
end
respond_to do |format|
format.html #index.html.erb
format.xml { render xml: @tweets }
end
end
# Inside our routing file:
get '/local_tweets/:zipcode' => 'tweets#index'
get '/local_tweets/:zipcode' => 'tweets#index', as: 'local_tweets'
# then to link to the local tweets path:
# <%= link_to "Tweets in 32828", local_tweets_path(32828) %>
# Route Parameters:
=begin
to get tweets from different users ie:
/eallam
/envylabs
/greggpollack
/github
etc.
=end
get ':name' => 'tweets#index', as: 'zombie_tweets'
# then in .erb:
# <%= link_to "Gregg", zombie_tweets_path('greggpollack') %>
# Since we're calling the index action, we need to go into that controller
# /app/controllers/tweets_controller.rb
def index
if params[:name]
@zombie = Zombie.where(name: params[:name]).first
@tweets = @zombie.tweets
else
@tweets = Tweet.all
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment