Rails Intro


 In views:
  = debug(@movie)
  = @movie.inspect


create just combines new and save

Once created, object acquires a primary key (id column in every AR model table)"
–  if x.id is nil or x.new_record? is true, x has never been saved"
–  These behaviors inherited from ActiveRecord:: Base—not true of Ruby objects in general"

EXAMPLE
Assume table fortune_cookies has column fortune_text Which of these instance methods of FortuneCookie < ActiveRecord::Base will not return a silly fortune (if any)?
☐ def silly_fortune_1
      @fortune_text + 'in bed'      
 // this returns an instance variable. but the whole point of active record is to manipulate records in the database.
end
☐ def silly_fortune_2
    self.fortune_text + 'in bed'                  //this is a method call (getter)
end
☐ def silly_fortune_3
     fortune_text + 'in bed'                        //this is a method call (getter)
end
☐ They will all return a silly fortune


The singular name is used for the class uppercase. Singular name is used for the filename lowercase and then the pluralized version lowercase is used for the database table.

Model: CRUD

C: Database Migrate:

 for database schema update, and keeping its version control and roll backs. DB knows which version of migration it has applied so far and it is idempotent to as many times you want to call it, it knows to only do it once.
$ rails generate migration CreateMovies
if it is a new model you have to create new file for it
app/models/movie.rb
$ rails db:migrate   
$ rake db:test:prepare    
 # DB test prepare is essentially the equivalent of saying, “ Hey, test environment. I’ ve applied new migrations to my database so just clone the schema that I have and make sure that all the tests see that new schema and the test database too.” Again, it’ s a piece of automation that lets you not have to worry about doing this manually and actually keep everything in line.
$ heroku db:migrate

R

Movie.where("rating='PG'")
Movie.where('release_date < :cutoff and
     rating = :rating',
     :rating => 'PG', :cutoff => 1.year.ago)              # give a string and a hash to fill it out.
Movie.where("rating=#{rating}") # BAD IDEA!

Queries are lazy and can be chained
kiddie = Movie.where("rating='G'")
old_kids_films = kiddie.where("release_date < ?",30.years.ago)

old_kids_films.each |f| do    end    # this is when the query is executed. not before

find_*

find by id:
Movie.find(3) #exception if not found
Movie.find_by_id(3) # nil if not found
dynamic attribute-based finders:
Movie.find_all_by_rating('PG')       
# you can have lots of attributes in method name and it will generated by method missing if attributes match model columns
Movie.find_by_rating('PG')
Movie.find_by_rating!('PG')        # throws exception

U

m=Movie.find_by_title('The Help')"
m.release_date='2011-Aug-10'
m.save!
OR update atomically (transaction) using a hash of all new values for different columns
m.update_attributes      :release_date => '2011-Aug-10'

D

m = Movie.find_by_name('The Help')
m.destroy    # like finalize in java let a call back to clean up, like foreign keys and ...
OR m.delete    # directly deletes instance
after destroyng, trying to modify the instance will raise an exception

Suppose we’ve done
movie = Movie.where("title='Amelie'")
Then another app changes the movie’s title in the database table directly. Just after that instant, the value of movie:
will not be updated automatically, but can be updated manually by re-executing 
movie = Movie.where("title='Amelie'")



We already know how to add a new model, or change things about an existing model. Now we want to add a new action. A new thing that you can do to a model. To add a new action, remember, what do you need in a model view controller system? Well, in order for the controller to do its work, you've got to be able to get from a URI, to which method in the controller is going to actually handle that, and we saw that the routing subsystem is where that gets done.
routes.rb :    the default setup essentially gives you the routes for all of the 'CRUD actions' on your models. You also need to add the actual code that does the thing

Remember that when we say, " Add controller code," most of the time, really where the action happens is in the model, right? The controller code is really just there to mediate between the views and what's in the model, so the code that you are adding in the controller tends to be pretty small.
You've probably already added the code in the model that does the actual work. All you need to do now is basically connect that to the rest your app.
make sure there is something for it to render

Controllers

one action can have multiple views associated with it

To add a new action to a Rails app
1. Create route in config/routes.rb if needed
2. Add the action (method) in the appropriate app/controllers/*_controller.rb
3. Ensure there is something for the action to render in app/views/model/action.html.haml

A route consists of both a URI and an HTTP method. A route URI may be generated by Rails URI helpers



==============================


==============================



$ cd ~/Documents
$ mkdir temp
$ rails new myrottenpotatoes -t   # to avoid generating default test directories as we want to use rspec.

if you go to http://localhost:3000/movies it says undefined route

$rake routes   # shows no route
modify config/routes.rb
  resource :movies
  root :to => redirect(‘/movies’)

$rake routes   # shows routes for movie

if you go to http://localhost:3000/movies now it says undefined controller

#####create controllers/movie_controller.rb
$ rails generate migration create_movies
    this creates    db/migrate/20140307141649_create_movies.rb
There add details of a movie
   def up
    create_table 'movies' do |t|
    t.string 'title'
    t.string 'rating'
    t.text 'description'
    t.datetime 'release_date'
    t.timestamps
    end
  end

  def down
    drop_tables 'movies'
  end

$ rake db:migrate

create models/movie.rb       #all model names should be singular
  class Movie < ActiveRecord::Base

  end


combination of the migration file  and this model file tells ruby that we have a model with that schema.
Now you can directly contact the model via rails console without any code.
$ rails console
> starWars = Movie.create!(:title => 'Star Wars Morty', :release_date => '25-4-1997', :rating => 'PG')
>Movie.all         #returns all movies

create controllers/movie_controller.rb
  class MoviesController < ApplicationController

  end


if you go to http://localhost:3000/movies  The action 'index' could not be found for MoviesController
class MoviesController < ApplicationController
  def index
    @movies =  Movie.all
  end
end
if you go to http://localhost:3000/movies template missing

create views/movies/index.html.haml       same name as the action the controller is performing

it is good to have a description in the first line of haml file
-# this file is /views/movies/index.html.haml

hi

%h2

%table#movies
  %thead
    %tr
      %th Movie Title
      %th Rating
      %th Release Date
      %th More Info
  %tbody
    - @movies.each do |m|
      %td= m.title
      %td= m.rating
      %td= m.release_date
      %td= link_to "Read more about #{m.title}", movie_path(m)

     
Summary
1. routes.rb
2. create migration  for database creation
3. create empty model file
4. we used console to create a movie
4. create controller
5. create index action
5. create viewer for the action of the controller

Create equivalent haml file instead of application.html.erb : application.html.haml
!!! 5
%html
  %head
    %title My Rotten Potato Stepped Through :)

Now let’s do the show action for movie, which shows details of one movie
add the show action to movies_controller.rb
  def show
    id = params[:id]    # e.g. 1 is the id of the movie will be here  movies/1
    @movie = Movie.find(id)
  end

to show the ‘title’ of the movie as its URL, we use find_by_title instead:

———————
the form to create movie has destination URL of movies_path because if you do $rake routes you see that /movies is the path for create action.
= form_tag movies_path          which URI is gonna receive the POST method when user clicks the submit button. the target of the form submission is movies_path
movies_path combined with http post verb, will post a form to the create action of the movies controller.
the label helper will take an active record object type and one of the attributes of that object and generate a useful label.

if you are unsure go to http://api.rubyonrails.org/ and search for a method e.g. label. and in this case find label helper method.
label(object_name, method, content_or_options = nil, options = nil, &block)
  = label :movie, :title, 'Title'       -----what that label is supposed to be for.
OR options_for_select gives more options for a select tag.


summary
1. the form_tag specifies the controller action that will receive the form posting.
2. the helpers work with ActiveRecord models to produce human friendly markup
3. there are special created markups for things like dates, and pop-up menus
4. set flash message in create action and show it in the application.html.haml
    - if flash[:notice]
      #notice.message= flash[:notice]
    - elsif flash[:warning]
      #warning.message= flash[:message]                 flash message would be given as a div

-------------------

debugger                         sets a debugger
$ rails s --debugger
p params            p prints a variable
p params['movie']
p params[:movie]      this is not because string and symbols are interchangeable but params is a special kind of construct that quacks like a hash, but doesn't care if you pass it a symbol or string. it's written so that at your convenience you can do either one.
date is 3 separate popups that rails puts them together when creatign an object. To test the hypothesis
(rdb:5) movie = Movie.new(params[:movie])
#<Movie id: nil, title: "SaaS the Movie", rating: "G", description: nil, release_date: "2011-03-07 00:00:00", created_at: nil, updated_at: nil>
n                         next line
c                          continue





edit
do a $rake routes, you'll know that edit route is movie/:id/edit. This can be generated using edit_movie_path in the show template

edit template is exactly equal to create template BUT
instead of movies_path it should be movie_path(@movie), :method => :put

Things like button_to or link_to are like macros that generate the proper html tag
browsers don't support any mechanism but get/post. BUT if we want to have delete method of html rails puts a hidden input element right there and tell which method it is intented to call.
= button_to "Delete", movie_path(m), :method => :delete, data: { confirm: "Are you sure?" }       #generates the following self-contained form with a single submit button that calls delete action.

<form method="post" action="/movies/9"  class="button_to">
<div>
<input
name="_method" type="hidden" value="delete" />
<input
data-confirm="Are you sure?" type="submit" value="Delete" />
<input
name="authenticity_token" type="hidden" value="FSwfhq9TCS7xyhyHx+1ltov8j4IDnQ+UjiqEfjLRsrM=" />
</div>
</form>
































Comments