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" 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. 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
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 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 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' 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
Controllersone 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
============================== rails multi page form
http://stackoverflow.com/questions/3619551/what-is-the-best-way-to-structure-a-multi-page-form-to-create-and-edit-models-in http://stackoverflow.com/questions/12889204/rails-way-of-breaking-down-a-large-form-into-multiple-steps ============================== $ 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
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"> |
Software Engineering ➼ Machine learning ➼ Data Science ➼ Product Leadership 🎯 > Tools - Business > Ruby > cs169 >