Ruby on Rails


$ mkdir myProject
$ cd myProject
$ rails new blog
./gemfile
./app/   
assets/[images, javascript, stylesheet]
controllers, models, views
./config/
databse.yml
route.rb
./db/     datasbe schema, migration files
./test/

$ cd blog
$ rails g controller posts    #g OR generate, naming convention: should be plural. action is an individual page for your controller( respected view files for your controller).    
./app/controllers/posts_controller.rb
./test/controllers/posts_controller_test.rb
       
./app/helpers/posts_helper.rb
./test/helpers/posts_helper_test.rb

./app/assets/javascripts/posts.js.coffee
./app/assets/stylesheets/posts.css.scss

$ vim app/controllers/posts_controller.rb
def index        # add index action (route) / index page
end

$ touch ./app/views/posts/index.html.erb   # create. erb: Embedded ruby. use html, embedded ruby, javascript, etc.... in your view files
$ vim ./app/views/posts/index.html.erb
<h1>Our Blog</h1>
<p>Hello World!</p>

create a route to access this in the browser
$ vim ./config/routes.rb    # routes for our posts resource: think of posts as resource where you can create, retrieve, destroy,...
resources :posts        
# create a set of 7 routes for us one of which is 'index', the total are index, show, new, create, edit, update and destroy (a.k.a actions)
# create a set of routes by calling resources and passing the symbol with the name of our controller which is posts

$ rails s      # s OR server
http://localhost:3000/
http://localhost:3000/posts  Takes us right into our index action (or view)
Ctrl+u to view source code



$ rails g model post title:string content:text         # model is in singular form (versus controller which is in plural form)
./db/migrate/20130706161915_create_posts.rb             
#migration file: timestamp_ create_posts table in the database: migration is a way to deal with DB rather than writing SQL. Table name is in plural form, hence posts
#creates title, content and timestamps (created at, updated at)
./app/models/post.rb                                                        #post model
./test/models/post_test.rb
./test/fixtures/posts.yml

$ rake db:migrate      # run the appropriate migration (create the SQL table)
$ vim ./app/models/post.rb     
attr_accessible :title, :content                
#specify which fields to be accessible [upon updating the posts model (records table)]: which fields are allowed to be saved via mass assignment? pass arguments of field names
#@deprecated: in rails 4.0 use strong parameters which moves authorization/authentication to control
$ rails c     # OR console        for CRUD operations
# to create a post, there are two ways: first create a post object
# we use symbols :title, :content to have only one string representing each which will later on makes things easier for comparison or addittion or hash modification. It's like String.intern() in java

> post = Post.new(:title => "Our first post", :content => "Content for our first post.")         # create post object
> post.save       # save the object into the datasbe
> Post.create(:title => "Our second post", :content => "Content for our second post.")     # create a post entry in database in one step instead of two steps (above)

> posts = Post.all       # retrieve all posts
> post = Post.find(2)             # get post with id = 2
> post.title
> post.content

> post.title = "our 2nd post"         # update in two steps
> post.save 
> post.update_attribute(:content, "Content for our 2nd post.")      # update in one step
> post.update_attributes(:title => "Our Second Post", :content => "Content for our second post.")    # update multiple attributes
> post.find(2)        # notice the difference in create time and update time

> post.destroy      # delete second post
> post.find(2)        # error
> exit



$ rake routes        # generate routes (specified in ./config/routes.rb). 7 differnet actions for our Posts controller
# index: display all of our posts    
# show display an individual post
# new: display a form to submit a new form
# create: dosn't have a view. just saves the post in the datasbe, then redirect to a different page
# edit: display a form for editing a post
# update to actually doing the updating in the databse. It doesn't have a view file. then redirect to a differnet page
# destroy: delete a post. Like create/update, does not have view. A link to destroy action to destroy a post, redirects to a differnet page or render something else


   Prefix        Verb   URI Pattern               Controller#Action
    posts         GET    /posts(.:format)          posts#index
                     POST   /posts(.:format)          posts#create
 new_post      GET    /posts/new(.:format)      posts#new
edit_post        GET    /posts/:id/edit(.:format) posts#edit
     post           GET    /posts/:id(.:format)      posts#show
                     PATCH /posts/:id(.:format)      posts#update
                       PUT    /posts/:id(.:format)      posts#update
                   DELETE /posts/:id(.:format)      posts#destroy

We also get named routes that refereing to tese routes
posts_path   refer to index action
new_post_path   new action. create a link to new_post_path and that will link them to the new page
edit_post_path(1)     takes a parameter of hte post that you want to edit, rediect to a page to edit post w/ id 1
post(1)    take you to page to viw post     1

create these blank acntions and their vew files
$ vim ./app/posts_controller.rb
 #----------- 7 different actions
  def show  # show indivisual post
  end
 
  def new # display a anew form to create  a new post
  end
 
  def create # form from the new action will submit to and this will save the post into the datasbe
  end
 
  def edit # display a form for editing the pst
  end
 
  def update # to actually updatign a post. the form on edit page ill submit to the update action and this will update that post in the datasbe
  end
 
  def destroy # to delete a post
  end

$ touch ./app/views/posts/show.html.erb
$ touch ./app/views/posts/new.html.erb
$ touch ./app/views/posts/edit.html.erb



To change our posts index page to display all our posts instead of the HelloWorld message
In the controller change the index action to retrieve all of the posts and then make them available inside of the view file and then we can display them to the browser. To send something to your view you can create an instance variable

$ vim ./app/controllers/posts_controller.erb
def index   
    @posts = Post.all # get all posts and store them in this te @posts instance variable. then we can have access to this variable inside our view file to display these posts in the browser
  end
$ vim ./app/views/index.html.erb       # write embedded ruby  inside <% %> you can write ruby code, when you want to output somethign to the browser use <%= %>
<h1>Our Blog</h1>

<% @posts.each do |post| %>    <!-- loop through posts. posts is a hash that is retrieved from the datasbe in the ./app/controllers/post_controller.rb in the index action method.-->
    <h2> <%= post.title %> </h2>
    <p> <%= post.content %></p>
    <hr/>
<% end %>      <!-- close the loop block-->
$ rails s  # start the server

http://localhost:3000/posts       # there will be one post as we deleted the second post.

while the server is running open another terminal
$ rails c
$ Post.create(:title => "Our second post", :content => "Our secodn post content")
Refresh browser



View just one post at a  time. 'show' view.

$ vim ./app/controllers/posts_controller.rb
 def show  # show indivisual post
    @post = Post.find(params[:id])  # Params hash and grab the id that was passsed-in through the URL.
    # Note that INSTANCE VARIABLES ARE ACCESSIBLE INSIDE OUR VIEW FILE.
  end

$ vim ./app/views/posts/show.html.erb
<h1><%= @post.title %></h1>
<p><%= @post.content %></p>
<small><%= @post.created_at %></small>

modify index to include links to view the blog
$ vim ./app/views/posts/index.html.erb
<h1>Our Blog</h1>

<% @posts.each do |post| %>    <!-- loop through posts. posts is a hash that is retrieved from the datasbe in the ./app/controllers/post_controller.rb in the index action method.-->
    <h2> <%= link_to post.title, post %> </h2>   <!--link_to method text, id or object to get its show URL-->
    <p> <%= post.content %></p>
    <hr/>
<% end %>      <!-- close the loop block-->



Creating forms, redirecting users, rendering views, displaying flash messages.
Add a link in the index page to link to our posts new action which is going to be used to add a new post. 'new' action used to display a form so that one can create a post

Creating forms
first: edit the index page to add a link to this 'new' action
$ vim ./app/views/posts/index.html.erb
<p><%= link_to "Add a New Post, new_posts_path" %></p> <!-- link to the named route to the new action -->

$ vim ./app/views/posts/new.html.erb
<h1>Add a New Post</h1>
This link will take us to http://localhost:3000/posts/new

--

Now, let's add a form on the above page so that we can actually submit the post
$ vim ./app/controllers/post_controller.rb 
  def new
    @post = Post.new   # this will accessible in our 'new' action view
  end

$ vim ./app/views/posts/new.html.erb
<h1>Add a New Post</h1>

<%= form_for @post do |f| %> <!--Pass to it the empty post object that we created in the 'new' action in the controller. It takes a block that is used to creatign a form.-->
  <!--Here we have access to this local f variable. Create form fields for enterign a  new post.-->
  <p>
    <%= f.label :title %> <br/>  <!--Create a label tag for the title.-->
    <%= f.text_field :title %>
  </p>
 
  <p>
    <%= f.label :content %> <br/>  <!--for the content.-->
    <%= f.text_area :content %>
  </p>
 
  <p> 
    <%= f.submit "Add a New Post" %>   <!--Submit button.-->
  </p>

<% end %> <!--end the block-->

view it at http://localhost:3000/posts/new

--

Question: in the previous step we didn't do anything with the empty post object that we created in the 'new' action in the controller inside the 'new' action view?

Now get the created new post object at 'create' action. Grab the form values that were submitted and save it in the database.    
$ vim ./app/controllers/post_controller.rb 
    def create # form from the new action will submit to and this will save the post into the datasbe
    @post = Post.new(params[:post])  #he actual post information that was submitted throguh the form. And we grab that through the params hash and we grab that post information. Now we have access to the post object that we save to the databse.
   
    if @post.save    # returns true or false if save was successfull
       redirect_to posts_path, :notice => "Your post was saved."    # if was successfully saved. redirect to the named rout for the index action view.
       # notice is a flash message and we need to make sure we handle flash messages. Edit the layout file.
    else
      render "new"      # save has been usuccessfull. render the new form again so that the user can try again
    end
  end

$vim ./app/views/layouts/application.html.erb
<%= flash.each do |key, value| %> <!--We have access to this flash hash amd loop through it and pass it a block access its key and values-->
  <p><%= value %></p>  <!--key is hte type of message, value is The actual message itself.-->
<% end %>




Edit posts

Add Edit link in index view for each post.
$ vim ./app/views/posts/index.html.erb
 <p> <%= link_to "Edit", edit_post_path(post) %></p>       # in the for loop going through each post
$ vim ./app/views/posts/edit.html.erb
<h1>Edit Post</h1>

Create the form
$ vim ./app/views/posts/edit.html.erb
<h1>Edit Post1</h1>

<%= form_for @post do |f| %>
    <p>
      <%= f.label :title %> <br/>
      <%= f.text_field :title %>
    </p>
    <p>
      <%= f.label :content %> <br/>
      <%= f.text_area :content %>
    </p>
    <%= f.submit "Update Post" %>
<% end %>

$ vim ./app/controllers/post_controller.rb 
  def edit # display a form for editing the pst
    @post = Post.find(params[:id])
  end
 
  def update # to actually updatign a post. the form on edit page ill submit to the update action and this will update that post in the datasbe
    @post = Post.find(params[:id])

    if @post.update_attributes(params.require(:post).permit(:title, :content))
      redirect_to posts_path, :notice => "Your post has been updaetd"
    else
      render "edit"
    end
  end

The link would be in this form http://localhost:3000/posts/6/edit




Delete Posts

Add delete link to each post
$ vim ./app/views/posts/index.html.erb
<p> <%= link_to "Edit", edit_post_path(post) %> |
      <%= link_to "Delete", post, :confirm => "Are you sure", :method => :delete %>  <!--Confirm is for a javascripy popup. method is to go to the destroy action rather than the show action-->
</p>

$ vim ./app/controllers/post_controller.rb 
  def destroy # to delete a post
    @post = Post.find(params[:id])
    @post.destroy
    redirect_to posts_path, :notice => "Your post has been deleted."
  end




Validation. e.g. empty post title
$ vim ./app/views/posts/new.html.erb    and $ vim ./app/views/posts/edit.html.erb
   <% if @post.errors.any? %>

        <h2>Errors:</h2>

        <ul>
          <% @post.errors.full_messages.each do |msg| %>
              <li><%= msg %></li>
          <% end %>
        </ul>

    <% end %>








No difference. render :template => 'some/thing' is the same as just render 'some/thing', as well as the same as render :action => 'thing' if we are in the 'some' controller.

From RubyOnRails guides, all of the above is the same :

render :edit
render :action => :edit
render 'edit'
render 'edit.html.erb'
render :action => 'edit'
render :action => 'edit.html.erb'
render 'books/edit'
render 'books/edit.html.erb'
render :template => 'books/edit'
render :template => 'books/edit.html.erb'
render '/path/to/rails/app/views/books/edit'
render '/path/to/rails/app/views/books/edit.html.erb'
render :file => '/path/to/rails/app/views/books/edit'
render :file => '/path/to/rails/app/views/books/edit.html.erb'



Undo

$ rails generate controller FooBars baz quux
$ rails destroy  controller FooBars baz quux       # these two commands undo Controls

$ rails generate model Foo bar:string baz:integer # undo Models
$ rails destroy model Foo

$ rake db:migrate
$ rake db:rollback # undo rake
$ rake db:migrate VERSION=0                  # the version numbers come from listing the migrations sequentially.
















Comments