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.

2 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. Hi
    I think there is little type in
    /controller/init.rb
    line 17

    menu << ...<\n"

    ReplyDelete