Tags: , | Categories: Ruby on Rails, Ruby Posted by Admin on 2/6/2011 10:18 PM | Comments (0)

View basic

Lets start with generating a demo project called demo 3
rails new demo3	
	
After generation completed, we could add post model with title and body fields
rails g scaffold post title:string body:text
	
<!DOCTYPE html>
<html>
<head>
  <title>Demo3</title>
  <%= stylesheet_link_tag :all %>
  <%= javascript_include_tag :defaults %>
  <%= csrf_meta_tag %>
</head>
<body>

<%= yield %>

</body>
</html>
	
	
go to views/layouts yield basically appends all the rendering text from the views go to the views/posts/index.html.erb file
<h1>Listing posts</h1>

<table>
  <tr>
    <th>Title</th>
    <th>Body</th>
    <th></th>
    <th></th>
    <th></th>
  </tr>

<% @posts.each do |post| %>
  <tr>
    <td><%= post.title %></td>
    <td><%= post.body %></td>
    <td><%= link_to 'Show', post %></td>
    <td><%= link_to 'Edit', edit_post_path(post) %></td>
    <td><%= link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete %></td>
  </tr>
<% end %>
</table>

<br />

<%= link_to 'New Post', new_post_path %>
	
Filters before after around is executed both before and after an action is executed
private 
	def get_and_respond_with
		@post =  params[:id].present? ? Post.find( param )
		yield
		respond_with(@post)
	end
	
Place private keyword to the end of your rails controller otherwise all the methods underneath it will become private members. Private members are there for to not to serve a actions to the controllers we can have a rendering login in before or after action, which needs to be You can use
layout "my_layout"
	
to define a layout used all over the post controller which will search in the application folder or simply create a new view under the application called “posts.html.erb” which will be automatically picked up by convention or you can also define which template to use in an action
def index
    @posts = Post.all
    render :layout => 'juicy'
end
	

Partials

Add the following line under index.html.erb
<%= render 'category_list' %>
	
place a new layout view under posts called _category_list.html.erb and place some html Now navigate to /posts, you will see your extra html is rendered onto the page. underscore ( “_” ) sign to the trick to indicate that it is a partial view. Lets change the _category_list.html.erb file and add the following code
<h1> Category </h1>
<% categories.each do |c| %>
  <li>	<a href="#"> <%=c %> </a> </li>
<%end%>	
	
undefined local variable or method `categories' for #<#<Class:0xa98d474>:0xa98ba98> because we did not defined our categories variable we get an error. Lets define the variable in our render call replace the render ‘category_list’ with
<%= render :partial => 'category_list' , :locals => { :categories  => [ "test" , "test2" ] }  %>	
	

:locals is the data that we are passing into the partial view and the only data is named “categories” with an array of values we can also use layout to render the partial view create a new file called _green_box.html.erb in posts folder and place the following html code

<div style="background-color:green">
<%= yield %>
</div>
	

and modify the render line with

<%= render :partial => 'category_list' , :locals => {:categories => [ "test" , "test2" ] } ,  :layout =>  'green_box'  %>	
	
and refresh your posts page!

Helpers

bahadir@ubox:~/RubymineProjects/demo3$ rails g migration add_author 
      invoke  active_record
      create    db/migrate/20110206174947_add_author.rb
	  	

Open up the add_author.rb

class AddAuthor < ActiveRecord::Migration
  def self.up
  	add_column  :posts , :author_first , :string
  	add_column  :posts , :author_last , :string
  end
  def self.down
	remove_column  :posts , :author_first , :string
	remove_column  :posts , :author_last , :string
  end
end
	
This is a migration script that will help us to create the author_first and author_last column when we migrate up ( self.up ) and remove them ( self.down ) if we want to migrate down rake db:migrate
bahadir@ubox:~/RubymineProjects/demo3$ rake db:migrate
(in /home/bahadir/RubymineProjects/demo3)
==  AddAuthor: migrating ======================================================
-- add_column(:posts, :author_first, :string)
   -> 0.0008s
-- add_column(:posts, :author_last, :string)
   -> 0.0005s
==  AddAuthor: migrated (0.0016s) =============================================
	

Now go to _form.html.erb

  <div class="field">
    <%= f.label :author_first %><br />
    <%= f.text_field :author_first %>
  </div>
   <div class="field">
    <%= f.label :author_last %><br />
    <%= f.text_field :author_last %>
  </div>
  

and last thing to do is to add the fields to the show.html.erb file

<p>
	<b>Author first:</b>
	<%= @post.author_first %>
</p>
<p>
	<b>Author last:</b>
	<%= @post.author_last %>
</p>
	
Lets look at how we can use helpers go to your index.html.erb and add the following line
<td><%= author_name(post) %></td>
	
and open up the helpers/posts_helper.rb and define the following method
module PostsHelper
	def author_name(post)
		"#{post.author_first} #{post.author_last}"
	end
end
	
Lets print out the date of the post as well. Open up helpers/application_helper.rb and add the pretty_date function
module ApplicationHelper
	def pretty_date(date)
		date.strftime("%B %d %Y")
	end
end	
	
now go back to index.html.erb file and change the author_name line by appending the date of the post
<td><%= author_name(post) %> - <%= pretty_date(post.created_at) %> </td>
Other View Engines - HAML add the haml gem to the GEMFILE file
	gem 'haml'
	
and run the script
	bundle install
	
and restart your server create a new file called index.html.haml and add the following lines
%h1 Welcome
- @posts.each do |post|
	- if (should_show?(post))
		%h2= post.title
		%i #{author_name(post)} - #{pretty_date(post.created_at)}
		%hr
		= link_to 'Show', post |
		= link_to 'Edit' , edit_post_path(post)
		= link_to 'Destroy', post, :confirm => 'Are you sure?' , :method => :delete
%br
= link_to 'New Post', new_post_path
	
Holy cow! http://html2haml.heroku.com/ Holy cow #2 some more refactoring to the index.html.haml
%h1 Welcome
= render :partial => "category_list", :locals => { :categories => ["thing","thing2"] }

// you might not use as if you name the partial view as "post" but if you need to 
// use the as parameter to pass in the collection item
= render :partial => 'post' , :collection => @posts , :as => :post 
%br
= link_to "New Post", new_post_path	
	
Create a new file called _post.html.haml and paste in
- if (should_show?(post))
	%h2= post.title
	%i #{author_name(post)} - #{pretty_date(post.created_at)}
	%hr
	= link_to "Show", post |
	= link_to "Edit" , edit_post_path(post)
	= link_to "Destroy", post, :confirm => "Are you sure?" , :method => :delete	
	
Tags: , , , | Categories: Ruby on Rails, Ruby Posted by Admin on 2/6/2011 2:17 PM | Comments (0)

Choosing an IDE or Editor

I have downloaded the RubyMine from Jetbrains who are the makers of famous ReSharper for .NET guys and IntelliJ for Java. I generally like the products of Jetbrains but I see RubyMine is still in its early stages and not free. You may still download the product and try it for 30 days.

Frankly, I see RubyMine tries to do a lot, download bundles run rails, use any rails generators but since we are in the learning stages and understanding under the hood, it is better to go with a basic normal editor.

The editor I am going to use is BlueFish which I mentioned before. BlueFish is a very nice simple editor which makes it easy to browse folders, create files, and also nice syntax highlighting for different languages ( html , javascript , java , ruby , PHP, PERL , Python )

Folder tree is refreshed automatically if you click one time to the folder ( wow! ) It is very handy if you use rails through command line!

New Rails project

Lets start with writing code. First of all we need to create a new rails application. In order to do that open up a console and run the following command

rails new demo1

creates a new demo1 project. The project is empty which means there are no mappers, controllers or views are created for you. New command only generates a basic Rails project structure.

Mapper

config/routes.rb here you define a new route for the request handling

  • open up the config/routes.rb
  • Go to line 51
  • root :to => "home#index"
  • uncomment the root and change the string with home#index

Basically with this piece of code we tell rack that we will have a home controller and in that home controller there will be method called index. Lets create that controller first

  • go to app/controllers
  • add a new file called home_controller.rb
  • in that file place the following line of code
class HomeController < ApplicationController
   def index
     @message = " Hello from the home controller"
   end
end

We don’t want to place any html inside the controller, so let’s create our view as well. The file extension of the html file will be .erb which stands for eRuby that is the ruby templating file like asp or jsp

  • go to app/views/
  • create a folder called home
  • create a file named index.html.erb
  • and place the following code.
<h1>
	<%=@message%>
</h1>

It is important to understand that all the files and code placed inside is a convention.

  • home_controller.rb maps with root :to => "home#index"
  • and the class
  • HomeController maps with root :to => "home#index"
  • def index maps with root :to => "home#index" and index.html.erb as well
To try how things will be broken, just rename one of the following into “homex” and see the errors.
If you place everything fire up rails in console using
rails s
in your application directory.

Using Query Strings

Make a request to your home http://localhost:3000/?m=testtest Rails contains an object called params and to get the value of the m query string

@user_messsage = params [ :m ] 

Open your index.html.erb to modify the html and print out the parameter that passed in ( which is a bad idea to directly output the value )

 <h1> <%=@message%> & <%=@user_message%>  </h1>

and if you refresh you browser you will see

Hello from the home controller & testtest

but if you pass in an html tag with your query string , Rails 3 will automatically encode the output http://localhost:3000/?m=testtest<h2>

Forms

Create a form and post the information to server and back Create a new file called new.index.erb under your views/home folder paste the following code ( normal way, not rails way! )

<form action="/home/create" action="post">
  <input name="message" />
  <input type="submit" value="submit" />
</form>

and we need to map this action in our routes file.

  • Open up config/routes.rb
  • Add the following line under root ( line 51 )
  • match "home/new" => "home#new"
  • means that when you receive a request to home/new call the home controller’s new action.
  • Lets define our action in home_controller.rb
    def new
    end
    				
  • fire up the browser and navigate to http://localhost:3000/home/new
  • you should see a simple textbox with a submit button and when you press the button, you will get an error says that
Routing Error
No route matches "/home/create"

Routing

Rather than mapping all the actions one by one, we may remove ( comment ) the match line and active the line 57

match ':controller(/:action(/:id(.:format)))'

which will do the auto mapping for us. ( sounds familiar with ASP.NET MVC global.asax routing definitions ? :)

We previously define basic html to post our data to the server, but in rails you don’t need to write that piece of code.

<% form_tag ( 'create' ) do %>
	<input name="message" />
	<input type="submit" value="submit" />
<% end %>

navigate to http://localhost:3000/home/new

def create
  	@message = params[:message]
  	# new in Rails 3 build in, use notice
	redirect_to "/" , :notice => @message
  end

and refactor index.html.erb into

<h1>
	<%=@message%>
</h1>
<%=notice%>

as you realized, we did not use the @user_message but use the notice session parameter which we set in the create method

CRUD

So far we went with the basics but the real power of rails starts with database operations and how easy to manage, generate and iterate. We start with the rails generator Scaffold

rails g scaffold pint name:string price:decimal

This will tell the rails that, hey rails 

 

  • I want have a new model called pint
  • Pint will have two fields which are
  • Name is a string
  • Price is a decimal field

 

Rails does not like singular form of models ( like pint in this scenario ) Here is the output rails 3 generates

bahadir@ubox:~/RubymineProjects/demo1$ rails g scaffold pint  name:string price:decimal
      invoke  active_record
      create    db/migrate/20110206113538_create_pints.rb
      create    app/models/pint.rb
      invoke    test_unit
      create      test/unit/pint_test.rb
      create      test/fixtures/pints.yml
       route  resources :pints
      invoke  scaffold_controller
      create    app/controllers/pints_controller.rb
      invoke    erb
      create      app/views/pints
      create      app/views/pints/index.html.erb
      create      app/views/pints/edit.html.erb
      create      app/views/pints/show.html.erb
      create      app/views/pints/new.html.erb
      create      app/views/pints/_form.html.erb
      invoke    test_unit
      create      test/functional/pints_controller_test.rb
      invoke    helper
      create      app/helpers/pints_helper.rb
      invoke      test_unit
      create        test/unit/helpers/pints_helper_test.rb
      invoke  stylesheets
      create    public/stylesheets/scaffold.css

After that check out the folders in your folder structure. Let’s migrate the database so that our model is also reflected ( huh did we have already a database setup ? :) Well, Sqlite is already in place and rails already made the setup for us.

rake db:migrate
bahadir@ubox:~/RubymineProjects/demo1$ rake db:migrate
(in /home/bahadir/RubymineProjects/demo1)
==  CreatePints: migrating ====================================================
-- create_table(:pints)
   -> 0.0018s
==  CreatePints: migrated (0.0019s) ===========================================
  • navigate to http://localhost:3000/pints
  • add couple of records now let’s do some trick
  • Open up pints_controller.rb
  • and the following 2 lines
    format.xml  { render :xml => @pints } after this.
    format.json { render :json=>@pints}
    format.text { render :text=>@pints}
    
  • and navigate to
  • http://localhost:3000/pints.json or http://localhost:3000/pints.text or http://localhost:3000/pints.xml

I am amazed how easy it is! Hold on a second, we did not setup any routing and application responds us. How could it be happen ? Open up routes.rb file Line 2 is added by the rails generator

resources :pints
which does the all the routing tricks for us.