Monday, October 24, 2011

Learning Ramaze (3) - Layout and View

I am learning Ramaze, a small web application framework based on Ruby. This is my third post for the topic.

My approach to learning Ramaze is first to (1) start with a skeletal application that Ramaze creates and then (2) modify it manually little by little to achieve a final application that I like; thereby, (3) understanding how Ramaze works along the way. Trying to understand Ramaze through reading its documentation simply does not work for me.

In this post, I describe my changes to the layout (/layout/default.xhtml). The reason for the changes is that I feel uncomfortable about roles of layouts and views so far because the distinction between the two seems somewhat blurred.

1. Resources

Ramaze: http://www.ramaze.net
My first post: Learning Ramaze (1)
My second post: Learning Ramaze (2)


2. Moving Footer Contents out of Layout

I understand that Layout in Ramaze is designed for common parts of multiple views. However, I would like to think that layout is about position, size, color, style and background etc. and that view is about contents such as text and image data. In other words, the role of a layout is about how to display something and the role of a view is about what to display within a given layout.

For example, @content variable is positioned in <body></body> section of the layout (/layout/default.xhtml) and gets replaced with the actual content of its view (/view/index.xhtml) when displayed in browser. The layout is playing a role of presentation and the view is playing a role of contents. That's good.

However, in the footer section of the layout, there are embedded contents such as copyright notice and a "powered by" statement like this:
# /layout/default.xhtml
<footer id="footer">
  Copyright &copy;2011 Socrateos | Powered by <a href="http://ramaze.net/">Ramaze</a>
</footer>
So I moved these contents out of the layout, replacing them with instance variables like this:
# /layout/default.xhtml
<footer id="footer">
  #{@copyright} | #{@poweredby}
</footer>
Then I put those texts in the controller (/controller/main.rb)
# /controller/main.rb
class MainController < Controller
  def index
    @title = 'DBAdmin'
    @subtitle = 'Home'
    @copyright = 'Copyright ©2011 Socrateos'                           # moved here from layout
    @poweredby = 'Powered by <a href=http://www.ramaze.net>Ramaze</a>' # moved here from layout
  end
end

class DataController < Controller
  map '/data' 
  def index
    @title = 'DBAdmin'
    @subtitle = 'Data'
    @copyright = 'Copyright ©2011 Socrateos'                           # moved here from layout
    @poweredby = 'Powered by <a href=http://www.ramaze.net>Ramaze</a>' # moved here from layout
  end
end

class DesignController < Controller
  map '/design'
  def index
    @title = 'DBAdmin'
    @subtitle = 'Design'
    @copyright = 'Copyright ©2011 Socrateos'                           # moved here from layout
    @poweredby = 'Powered by <a href=http://www.ramaze.net>Ramaze</a>' # moved here from layout
  end
end

class SystemController < Controller
  map '/system'
  def index
    @title = 'DBAdmin'
    @subtitle = 'System'
    @copyright = 'Copyright ©2011 Socrateos'                           # moved here from layout
    @poweredby = 'Powered by <a href=http://www.ramaze.net>Ramaze</a>' # moved here from layout
  end
end

Now, these variables never change from page to page. So I do not want to repeat these assignments in every controller. Ramaze actually creates two initial controllers in /controller directory: main.rb and init.rb. And I assume init.rb is called when any controller is instantiated. So if I place these assignment statements in init.rb just once, then it should work for every page. And it does. (I also moved @title here.)
# /controller/init.rb
class Controller < Ramaze::Controller
  ...
  def initialize                        # 2011.10.23 created
    super
    @title = 'DBAdmin'   # 2011.10.23 moved here from main.rb; 2011.09.10 - changed title
    @copyright = 'Copyright ©2011 Socrateos'                           # moved here from layout
    @poweredby = 'Powered by <a href=http://www.ramaze.net>Ramaze</a>' # moved here from layout
  end
end
And controllers in main.rb are now simpler and have no repeating.
# /controller/main.rb
# 2011.10.23 moved variables @titile, @copyright and @poweredby to init.rb#initialize
class MainController < Controller
  map '/'       # Not required, but I prefer explicit statement; it increases clarity of intention.
  def index
    @subtitle = 'Home'
  end
end

class DataController < Controller
  map '/data'
  def index
    @subtitle = 'Data'
  end
end
  
class DesignController < Controller
  map '/design'
  def index
    @subtitle = 'Design'
  end
end

class SystemController < Controller
  map '/system'
  def index
    @subtitle = 'System'
  end
end
3. Moving Navigation Menu Contents out of Layout

The menu items, "Home", "Data", "Design" and "System", are also contents, which I think belong to View, not Layout. So i take them out from the the layout. The layout file /layout/default.xhtml was like this:
...
<nav class="grid_6 last">
<ul class="clearfix">
<li><a href="http://localhost:7000/">Home</></li>
<li><a href="http://localhost:7000/data/">Data</a></li>
<li><a href="http://localhost:7000/design/">Design</a></li>
<li><a href="http://localhost:7000/system/">System</></li>
</ul>
</nav>
...
But now it looks like:
...
<nav class="grid_6 last">
<ul class="clearfix">
#{@menu}    # 2011.10.28 replaced hard-coded menu items. See controller for its content.
</ul>
</nav>
...
Like @title variable, I will initialized @menu variable in the parent controller (in init.rb). I created build_menu() method to generate a text with full menu content. I now use relative paths such as "\data" instead of absolute paths such as "http://localhost:7000/data". I also made a minor change to the title. It is now "Database Manager" instead of "DBAdmin".
# /controller/init.rb
#
class Controller < Ramaze::Controller
  ...
  def initialize
    super
    @title = 'Database Manager' 
    ...
    @menu = build_menu        # 2011.10.28 menu contents set here, removed from layout
  end
  
  def build_menu
    items = {'Home' => '/', 'Data' => '/data', 'Design' => '/design', 'System' => '/system'}
    menu = ''
    items.each do |key,value|
      menu << "<li><a href=\"#{value}\">#{key}</a></li<\n"
    end
    return menu
  end
  
end


4. Underlining Current Menu Item  I have created @subtitle to display which page users are looking at currently. So for example, if user click "Data" menu item, the page displays a big "Data" subtitle. I would like to make a minor change to the menu so that it will underline the current menu, another indication of current page for users. Here is the updated version.
# /controller/init.rb
#
class Controller < Ramaze::Controller
  ...
  def initialize
    super
     ...
    @menu = ''  # 2011.10.29 each controller generates its own menu
  end
  
  def build_menu(current_menu)
    items = {'Home' => '/', 'Data' => '/data', 'Design' => '/design', 'System' => '/system'}
    menu = ''
    items.each do |key,value|
       u1 = (key==current_menu)? '<u>':''
       u2 = (key==current_menu)? '</u>':''
      menu << "<li><a href=\"#{value}\">#{u1}#{key}#{u2}</a></li<\n"
    end
    return menu
  end
  
end
Now I have to generate menu for each controller.
# /controller/main.rb
class MainController < Controller
  map '/'
  def index
    @subtitle = 'Home'
    @menu = build_menu 'Home'
  end
end

class DataController < Controller
  map '/data' 
  def index
    @subtitle = 'Data'
    @menu = build_menu 'Data'
  end
end
data
class DesignController < Controller
  map '/design'
  def index
    @subtitle = 'Design'
    @menu = build_menu 'Design'
  end
end

class SystemController < Controller
  map '/system'
  def index
    @subtitle = 'System'
    @menu = build_menu 'System'
  end
end
And now it looks line this:















5. Summary


1. Layout is used for common parts of multiple views.
2. Layout can also be thought as a presentation layer of display, specifying positions, sizes, and colors and styles whereas views can be thought as contents of display. In other words, layout is about HOW to display; views are about WHAT to display.
3. The menu item contents and footer contents are embedded in a layout in a skeletal app Ramaze creates.
4. One way to remove contents (such as menu items and footer) out of layout is to use instance variables in layout and initialize them in controllers.
5. If the value of instance variable does not change between pages, it can be initialized in the parent controller (Controller#initialize in /controller/init.rb).

6. Next

I have now completed four main pages that correspond to my main menu items: Home, Data, Design, and System. Next I would like to learn how to control and display the body part of each of these pages.

Saturday, October 22, 2011

Learning Ramaze (2) - Adding More Pages

I am learning Ramaze, a small Ruby web framework. This post continues what I started in the previous post, Learning Ramaze (1).

As described in my previous post, my approach to learn Ramaze is to start with a skeletal Ramaze app and to make changes bit by bit to my liking, learning how Ramaze works along the way.

In this second post, I describe what I did to add more pages to the application.
My navigation menu has 4 items: Home, Data, Design, System. I want separate pages for these menu items.

Basically, I create a controller and a view file for each of these three new pages. Ramaze then takes care of the rest.


1. Resources

Ramaze: http://www.ramaze.net
Learning Ramaze (1): My previous post that started Leaning Ramaze series.



2. Display Page Title


Currently the instant variable @tile is used to display the title, "DBAdmin". Since the application is going to have multiple pages, I want to display a page title also. Then, I will know immediately which page I am looking at. To do this, I edited /layout/default.xhtml and added a new instance variable @subtitle, which can be "Home", "Data", "Design" or "System".

# /lauout/default.xhtml

  <head>
    <title>#{@title}/#{@subtitle}</title> # added subtitle
    ...
    </head>
  <body>
    ...
    <header class="grid_6">
      <h2>#{@title}</h2>                  # used for application title (in smaller font)
      <h1>#{@subtitle}</h1>               # added to display current page (in larger font)
    </header> 
  ... 
So the header section of a page looks like this:






3. Create Controllers For New Pages

Edit /controller/main.rb to add controllers for new pages: DataController, DesignController, and SystemController. Note that I added @subtitle variable to display user's current page (menu).


# /controller/main.rb

class MainController < Controller
...  
 def index
    @title = 'DBAdmin'
    @subtitle = 'Home'     # display current page in multi-page application/site
  end
...
end


class DataController < Controller
  map '/data'               
  
  def index                # Ramaze will find its content in /view/data/index.xhtml
    @title = 'DBAdmin'
    @subtitle = 'Data'     # display current page in multi-page application/site
  end
  
end


class DesignController < Controller
  map '/design'
  
  def index                # Ramaze will find its content in /view/design/index.xhtml
    @title = 'DBAdmin'
    @subtitle = 'Design'   # display current page in multi-page application/site
  end
  
end


class SystemController < Controller
  map '/system'
  
  def index                # Ramaze will find its content in /view/system/index.xhtml
    @title = 'DBAdmin'
    @subtitle = 'System'   # display current page in multi-page site
  end
  
end


4. Create View Files For New Page Contents

Now I create view files, one for each of new pages to display its contents.
All these four files have same name, index.xhtml, and placed in its own sub-folder under /view folder. For example, the data view file (index.xhtml) will be placed in /view/data.

So my view directory becomes like this:















And each view file looks like the followings:
# view/data/index.xhtml

<p>
    DBAdmin is a web tool to manage PostgreSQL databases.
</p>

<p>
    Here we can: View/Add/Edit/Delete Database Records
</p>

<ul>
    <li>
       View - displays query results
    </li>
    <li>
       Add - add new record(s)
    </li>
    <li>
       Edit - modify existing record(s)
    </li>
    <li>
       Delete - delete record(s)
    </li>
</ul>
# view/design/index.xhtml

<p>
    DBAdmin is a web tool to manage PostgreSQL databases.
</p>

<p>
    Here we can design database structure.
</p>

<ul>
     <li>
       Databases - create, alter, drop database(s)
     </li>
     <li>
       Tables - create, alter, drop table(s)
     </li>
     <li>
       Roles - create, alter, drop role(s)
     </li>
</ul>
# view/system/index.xhtml

<p>
    DBAdmin is a web tool to manage PostgreSQL databases.
</p>

<p>
    Here we can modify web interface.
</p>

<ul>
    <li>
       UI Templates - select preferred UI template
    </li>
    <li>
       Add-Ons - add new capabilities through add-ons
    </li>
    <li>
       Etc.
    </li>
</ul>



5. Results of Changes

Here are the results of the changes.




















6. Another way

Instead of creating controllers, sub-folders and index.xhtml files,I could have used additional methods within MainController and created corresponding view files in the same /view folder.

# /controller/main.rb
#
class MainController < Controller

  def index
    @title = 'DBAdmin'
    @subtitle = 'Home'
  end
  
  def data
    @title = 'DBAdmin'
    @subtitle = 'Data'
  end

  def design
    @title = 'DBAdmin'
    @subtitle = 'Design'
  end

  def system
    @title = 'DBAdmin'
    @subtitle = 'System'
  end

end
Then I would create the corresponding view files (data.xhtml, design.xhtml, and system.xhtml) placed directly in /view directory without sub-folders.

 This is much simpler. But using of controllers and sub-view folders is perhaps more scalable. Since my DBAdmin is going to be rather complex, I decided to use controllers and sub-folders for these main pages. Besides, a well-organized sub-directories of view gives an instant preview of basic structure of the application/site.


7. Summary

1. There are at least two way of creating multiple pages (besides static files).
2. One way of creating multiple pages is to use controllers, sub-view folders.
3. Another way of creating multiple pages is to use additional methods within the /controller/main.rb and corresponding view files directly under the /view directory.
4. Using of additional methods has advantage of simplicity.
5. Using controllers and sub-view folders has advantage of scalability.
6. Using controllers and sub-view folders has also advantage of providing an instant preview of basic structure of application/site.


8. Next

There are a few things that I would like to modify. For example, I initialize @title to "DBAdmin" - four times. Since the value of the variable never changes, its initialization should be done only once. Another thing that I would like to change is about usage of layout. Currently distinction between layout and view is blurred. I would like to make a clear distinction so that layout is used for display framework only and its view for contents only.

Tuesday, October 18, 2011

Installing Aptana Studio 3 on Ubuntu 11.04

I installed Aptana Studio 3 on my Ubuntu 11.04.

1. Resources.
Aptana home: http://www.aptana.com/

2. Install.
Downloaded Aptana Studio 3.
Unzipped to a folder "Aptana Studio 3"
Dropped the folder in /opt/ directory
Renamed the folder to "aptana3"

3. Placed a link in the Main Menu.

Sunday, October 16, 2011

Reinstalling Sequel

My installed Sequel was acting strange. It crashed hard to corrupt my file systems, resulting my Ubuntu not being able to start up. So I decided to reinstall it.


0. Resources
Sequel home page: http://sequel.rubyforge.org/


1. Uninstall Sequel
$ sudo gem uninstall sequel
Select gem to uninstall:
 1. sequel-3.26.0
 2. sequel-3.27.0
 3. All versions
> 3
Successfully uninstalled sequel-3.26.0
Successfully uninstalled sequel-3.27.0

2. Install Sequel
$ sudo gem install sequel
Successfully installed sequel-3.28.0
1 gem installed
Installing ri documentation for sequel-3.28.0...
Installing RDoc documentation for sequel-3.28.0...

3. Test Sequel
$ irb
irb(main):001:0> require 'sequel'
=> true
irb(main):002:0> DB = Sequel.sqlite
=> #
irb(main):003:0> DB.create_table :items do
irb(main):004:1* primary_key :id
irb(main):005:1> String :name
irb(main):006:1> Float :price
irb(main):007:1> end
=> nil
irb(main):008:0> items = DB[:items]
=> #
irb(main):009:0> items.insert(:name => 'iphone', :price => 450)
=> 1
irb(main):010:0> items.insert(:name => 'xphone', :price => 550)
=> 2
irb(main):011:0> items.insert(:name => 'zphone', :price => 650)
=> 3
irb(main):012:0> puts "Items count: #{items.count}"
Items count: 3
=> nil
irb(main):013:0> puts "Average price is: #{items.avg(:price)}"
Average price is: 550.0
=> nil

4. Test Sequel (continues)
irb(main):014:0> PGDB = Sequel.connect('postgres://localhost/mydb?user=socrateos&password=secret')
=> #
irb(main):015:0> PGDB.create_table :items do
irb(main):016:1* primary_key :id
irb(main):017:1> String :name
irb(main):018:1> Float :price
irb(main):019:1> end
=> nil
irb(main):020:0> pgitems = PGDB[:items]
=> #
irb(main):021:0> pgitems.insert(:name => 'Camera A', :price => 1000)
=> 1
irb(main):022:0> pgitems.insert(:name => 'Camera B', :price => 2000)
=> 2
irb(main):023:0> pgitems.insert(:name => 'Camera C', :price => 3000)
=> 3
irb(main):024:0> puts "Item count: #{pgitems.count}"
Item count: 3
=> nil
irb(main):025:0> puts "Average price: #{pgitems.avg(:price)}"
Average price: 2000.0
=> nil

5. Finish Testing Sequel
irb(main):026:0> DB.disconnect
=> []
irb(main):027:0> PGDB.disconnect
=> []
irb(main):028:0> quit

Saturday, October 1, 2011

Installing Ruby (Ruboto) on Sony Tablet S

I installed Ruboto (JRuby for Android) on Sony Tablet S.

1. Resouces.
Ruboto: http://ruboto.org/
Running a Ruby interpreter on iPad: http://stackoverflow.com/questions/2602697/running-a-ruby-interpreter-on-ipad

2. Installing Ruboto-IRB on the Sony Tablet.
It's so simple to install Ruboto-IRB.
(1) Go to the Market and search for Ruboto. 
(2) Download Ruboto-IRB.
(3) Open installed Ruboto-IRB
(4) Wait to see "Installing JRuby .... Done"
That's it.

3. Testing the IRB.
>> "Hello World" [Enter]
==> Helo World
>> 1+2 [Enter]
==> 3
>> s = "Ruby string" [Enter]
==> Ruby string
>> s.upcase [Enter]
==> RUBY STRING
Nice!