Beast demo

Neat list of different features is good, but if you really want to know how does Malline differ from ERB, you have to see real life example.

Beast is a small, light-weight forum in Rails with a scary name. I use it as an example, and rewrite some of it's ERB templates to Malline. Now when the ERB templates are from well-known software, and are not written by me, the compare may be considered to be quite fair and truthful.

On this page

Introduction

Examples are from the latest (at 2007-10-07) stable Beast, version 1.0. I have tested (quickly) that the outputs are identical from my Malline templates and original ERB templates (identical as in "It seems to look the same in Firefox" ;D). I converted all templates from app/views/user.

Just remember, I tried to keep the structure exactly the same, so it's easier to make the comparison. That is why that does also mean that these examples are not the best way to do things.

new.rhtml » new.mn

Let's start with the simplest file. As you can see, Beast uses Gibberish localization library, so that forces every string to be form "foo"[:bar].

form_for has it's own magical ERB-output features, so it's not a normal helper method and thus not prefixed with "_". Malline automatically emulates ERB and captures output correctly.

_render and _submit_tag both are just normal helper methods, so they are outputted by using the prefix. Nothing extraordinary exciting here.

1
2
3
4
5
6
7
8
<h1><%= 'Signup'[] %></h1>

<%= error_messages_for :user %>

<% form_for :user, :url => users_path do |f| -%>
  <%= render :partial => "form", :object => f %>
  <p><%= submit_tag 'Sign up!'[:signup_title] %></p>
<% end -%>
1
2
3
4
5
6
7
8
h1 'Signup'[]

_error_messages_for :user

form_for :user, :url => users_path do |f|
    _render :partial => "form", :object => f
    p { _submit_tag 'Sign up!'[:signup_title] }
end

_contact_info.rhtml » _contact_info.mn

This file is actually quite clean in ERB, and it's conversion is straightforward.

settings.foo-methods are rendered automatically. See configuration options for more info.

This is not the only file that uses this "p { label; br; settings.foo }" -pattern. Custom helper method for that could shorten and clean up template files, but since I'm trying to make everything equally to original design, I just get along without it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<h3><%= 'User Profile'[] %></h3>

<p>
  <label for="display_name"><%= 'Display Name'[:display_name_title] %></label><br />
  <%= settings.text_field :display_name %>
</p>

<p>
  <label for="user_identity_url"><%= 'Identity Url'[:identity_url_title] %></label><br />
  <p class="help"><%= 'Enter your OpenID Identity Url if you know it'[:open_id_field ]%>.</p>
  <%= settings.text_field :identity_url %>
</p>

<p>
  <label for="user_website"><%= 'Website'[:website_title] %></label><br />
  <%= settings.text_field :website %> 
    <span class="entryhelp">
    (<%= 'without http://'[:without_http] %>)
    </span>
</p>

<p>
  <label for="user_bio"><%= 'Bio'[:bio_title] %></label><br />
  <%= settings.text_area :bio, :rows => 10, :style => "width:99%" %>
</p>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
h3 'User Profile'[]

p do
    label 'Display Name'[:display_name_title], :for => 'display_name'; br
    settings.text_field :display_name
end

p do
    label 'Identity Url'[:identity_url_title], :for => 'user_identity_url'; br
    p.help 'Enter your OpenID Identity Url if you know it.'[:open_id_field] 
    settings.text_field :identity_url
end

p do
    label 'Website'[:website_title], :for => 'user_website'; br
    settings.text_field :website
    span.entryhelp '('+'without http://'[:without_http]+')'
end

p do
    label 'Bio'[:bio_title], :for => 'user_bio'; br
    settings.text_area :bio, :rows => 10, :style => 'width:99%'
end

edit.rhtml » edit.mn

Sorry about long lines in .rhtml, but I didn't want to modify the original files. This file is so much cleaner in Malline, because most of the ERB file is also just Ruby.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<% content_for :right do %>

<h5><%= 'Avatars'[:avatars_title] %></h5>

<p><%= 'To have your very own avatar displayed on this forum visit {gravatar} and sign up for a free gravatar.'[:gravatar_notice, %(<a href="http://www.gravatar.com/">gravatar.com</a>)] %></p>

<% end %>

<h1><%= 'Settings'[:settings_title] %></h1>

<p class="subtitle"><%= 'for {user}'[:for_user, @user.display_name] %>
    <% if @user.login!=@user.display_name %>
    (<%= @user.login %>)
    <% end %>
    </p>

<%= error_messages_for :user %>

<% form_for :user, :url => user_path(@user), :html => { :method => :put } do |f| -%>
  <%= render :partial => "settings", :object => f %>
  <p><%= submit_tag 'Change e-mail or password'[:change_email_or_password], :or => link_to( "cancel"[], "/") %></p>
<% end -%>

<br />

<% form_for :user, :url => user_path(@user), :html => { :method => :put } do |f| -%>
  <%= render :partial => "contact_info", :locals => { :settings => f } %>
  <p><%= submit_tag 'Update Profile'[], :or => link_to( "cancel"[], "/") %></p>
<% end -%>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
content_for :right do
    h5 'Avatars'[:avatars_title]
    p 'To have your very own avatar displayed on this forum visit {gravatar}
        and sign up for a free gravatar.'[:gravatar_notice,
        %(<a href="http://www.gravatar.com/">gravatar.com</a>)]
end

h1 'Settings'[:settings_title]

p.subtitle 'for {user}'[:for_user, @user.display_name] do
    _" (#{@user.login})" if @user.login != @user.display_name
end

_error_messages_for :user

form_for :user, :url => user_path(@user), :html => { :method => :put } do |f|
    _render :partial => "settings", :object => f
    p { _submit_tag 'Change e-mail or password'[:change_email_or_password],
        :or => link_to( "cancel"[], "/") }
end

br

form_for :user, :url => user_path(@user), :html => { :method => :put } do |f|
    _render :partial => "contact_info", :locals => { :settings => f }
    p { _submit_tag 'Update Profile'[], :or => link_to( "cancel"[], "/") }
end

_form.rhtml » _form.mn

This file is similar to _contact_info.mn, as it's conversion is very simple.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<p class="help"><%= 'Logins should start with least 2 characters and may consist of letters, numbers, or the underscore.'[:login_field] %></p>

<p>
  <label for="user_login"><%= 'Login'[:login_title] %></label><br />
  <%= form.text_field :login, :onkeypress => 'LoginForm.checkLogin(this)' %>
</p>

<p>
  <label for="user_email"><%= 'Email'[:email_title] %></label><br />
  <%= form.text_field :email %>
</p>

<p>
  <p class="help">
    <%= 'Enter your OpenID Identity Url if you know it.  If your Identity Url supports the {sreg} extension, you can {login_link} and create your account automatically'[:open_id_field_extended, %(<acronym title="Simple Registration">sreg</acronym>), link_to('login'[], login_path)] %>
  </p>
  <label for="user_identity_url"><%= 'Identity Url'[:identity_url_title] %></label><br />
  <%= form.text_field :identity_url %>
</p>

<p>
  <label for="display_name"><%= 'Display Name (optional)'[:display_name_title] %></label><br />
  <%= form.text_field :display_name %>
</p>

<div id="password_fields">
      <label for="user_password"><%= 'Password'[:password_title] %></label><br />
    <p class="help"><%= 'Enter your desired password twice.  It must be at least 5 characters.'[:password_field] %></span></p>

  <%= form.password_field :password %>
<span class="entryhelp">(<%= 'once'[] %>)</span><br />
  <%= form.password_field :password_confirmation %>
<span class="entryhelp">(<%= 'and then again'[] %>)</span>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
p.help 'Logins should start with least 2 characters and may consist of
    letters, numbers, or the underscore.'[:login_field]

p do
    label 'Login'[:login_title], :for => 'user_login'; br
    form.text_field :login, :onkeypress => 'LoginForm.checkLogin(this)'
end

p do
    label 'Email'[:email_title], :for => 'user_email'; br
    form.text_field :email
end

p do
    p.help 'Enter your OpenID Identity Url if you know it. If your Identity
        Url supports the {sreg} extension, you can {login_link} and create
        your account automatically'[:open_id_field_extended,
        %(<acronym title="Simple Registration">sreg</acronym>), link_to('login'[], login_path)]
    label 'Identity Url'[:identity_url_title], :for => "user_identity_url"; br
    form.text_field :identity_url
end

p do
    label 'Display Name (optional)'[:display_name_title], :for => 'display_name'; br
    form.text_field :display_name
end

div.password_fields! do
    label 'Password'[:password_title], :for => 'user_password'; br
    p.help 'Enter your desired password twice.  It must be at least 5 characters.'[:password_field]

    form.password_field :password
    span.entryhelp ' (' + 'once'[] + ')'; br
    form.password_field :password_confirmation
    span.entryhelp ' (' + 'and then again'[] + ')'; br
end

index.rhtml » index.mn

As the ERB files are getting longer, they also become more and more messy. Malline version of index is much cleaner and easier to maintain.

On lines 14 to 18 in index.mn there is need for :whitespace -modifier, that adds a space between each element. We don't want space before commas, so they are concatted manually.

On lines 22, 34, 48 and 52 we can use Rubys statement modifiers to conditionally render a tag. That eliminates need for additional "if end" -block.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<% content_for :right do %>
<h5><%= 'Find a User'[] %></h5>

<% form_tag users_path, :method => 'get' do -%>
<p>
<label><%= 'Display name or login'[] %></label>
<%= text_field_tag :q, params[:q] %> 
<%= submit_tag "Search"[:search_title] %></p>
<% end -%>

<% end -%>

<h1 style="margin-top:0;"><%= 'Users'[:users_title] %></h1>
<p class="subtitle">
<%= number_with_delimiter(@user_count) %> <%= 'users'[] %>, <%= @active %> <%= 'active'[] %>, <%= @user_count-@active %> <%= 'lurking'[] %>
</p>

<% if @user_pages.page_count > 1 -%>
<p class="pages"><%= 'Pages'[:pages_title] %>: <strong><%= pagination_links @user_pages, :window_size => 10 %></strong></p>
<% end -%>

<table border="0" cellspacing="0" cellpadding="0" class="wide forums">
  <tr>
    <th class="la" width="88%"><%= 'Name / Login'[:name_or_login] %></th>
    <th><%= 'Website'[:website_title] %></th>
    <th width="1%"><%= 'Posts'[:posts_title] %></th>
  </tr>
<% @users.each do |user|-%>  
  <tr>
    <td><%= link_to h(user.display_name), user_path(user), :class => (user.admin? ?<