Oren Eini

CEO of RavenDB

a NoSQL Open Source Document Database

Get in touch with me:

oren@ravendb.net +972 52-548-6969

Posts: 7,640
|
Comments: 51,260
Privacy Policy · Terms
filter by tags archive
time to read 4 min | 732 words

First things first, Harris Boyce III has done all the work, my sum contribution to this feature has included some wild cheering from the side lines.

That said, this is one cool feature. Let us explore it.

There are a lot of people who consider anything resembling <html> tags to be a mess, associate them with ASP Classic mistakes, and reject them out of hand. I think that this is a mistake, but I gave up changing the whole world overnight, now I have busy formulating three steps plans that takes a week...

At any rate, consider this output:

<html>
	<body>
		<table>
			<tr>
				<th>Names</th>
			</tr>
			<tr>
				<td>Ayende</td>
			</tr>
			<tr>
				<td>Rahien</td>
			</tr>
		</table>
	</body>
</html>

How would we generate this output using Brail? The classic approach is:

<html>
	<body>
		<?brail
                       component Grid, {"source": names}:
                         	section header:
                                    output "<tr><th>Names</th><tr>"
                               	end
                             	section item:
                        	    output "<tr><td>${item}</td><tr>"
                           	end
                        end
?> </body> </html>

This works, but it not really Jump-Up-And-Down-All-Excited code. And, of course, some people faint dead away from having to deal with raw HTML, "Give me my components or give me death!"

Now, here is the DSL way of doing it:

<?brail
dsl Html:
	body:
		component GridComponent, {"source" : names}:
			section header:
				tr:
					th:
						text "Names"
					end
				end
			end 
			
			section item:
				td:
					text item
				end
			end
		end
	end
end
?>

And this baby produces the same output as the previous one.

Now, you are probably aware that I am not really fond of raising the abstraction level needlessly, so why is this a good thing? Well, because it means that you can now do stuff like this:

dsl.Register(atom)
dsl:
   feed:
       title:
           div:
               text "some content"
           end
       end
   end
end

And it will be able to produce semantically correct ATOM feed. (To be exact, it would be if we had an AtomExtension implementation to the new IDslLanguageExtension interface, which we currently don't have).

There are some quirks in the implementation, but it is a very cool direction to go to.

time to read 1 min | 85 words

Right now Brail has no 1st class support for generating XML, like RoR's rxml. I just run into BooML, and it looks like a really great way to handle this issue. MonoRail doesn't (yet) has the notion of multiply DSLs, just standard templates and JS generation. Looks like that is the way I would go when I next need to support XML with MonoRail.

I knew there was a reason why I liked Boo so much :-)

time to read 8 min | 1508 words

On Castle Dev, Chris has asked the following question, how can you build the common "empty" template with Brail? NVelocity makes it very easy:

#foreach $item in $items

#each

      $item

#nodata

      No Data!

#end

But Brail makes it more cumbersome. There are solutions using view components, but they are too much, all too often. So, can we find an elegant solution to the problem?

As it turn out, yes (otherwise I probably wouldn't write this post :-) ). Brail is based on Boo, and Boo has extension methods... Another concept that Brail has is "Common Scripts", a set of helper methods that can be used across all the views in the application. Here is the script that I put in /Views/CommonScripts/extensions.brail:

[Boo.Lang.ExtensionAttribute]

static def IsEmpty(val as object) as bool:

      if val isa System.Collections.ICollection:

            return cast(System.Collections.ICollection,val).Count == 0

      end

      return true

end

Now you have extended all the objects in the application, so we can use it like this:

<%

    for item in items:

        output item

    end

    output "No Data" if items.IsEmpty

%>

I like that :-) The notion can be extended to more complex issues, naturally, such as ToXml extension method, etc. In fact, you can even forward calls to C# code, so that makes it even more powerful (I assume (and suggest against) that you aren't going to write a lot of code in the extensions).

time to read 3 min | 443 words

I have just finished putting together the second episode of Hibernating Rhinos. This one tooks several days and a lot of effort to produce. The download page is here, where you can also download the first episode, talking about Rhino Mocks.

The screencast is basically taking implementing similar functionality in both Web Forms and MonoRail, while I talk about the differences between the two approaches. I spent quite a bit of time explaining most of what I do when I am writing MonoRail code, so I hope it would be clear. This episode is not meant to be an introduction to MonoRail, it is merely a show & tell episode.

I am also afraid that I fell into the common trap of Web Froms vs. MonoRail comparision, and I focused quite a bit of my time on the UI layer, instead of focusing more on the controllers side of things, which is more important.

  • The overall length: 1:14:32
  • Download size: 60Mb
  • The big sigh at the beginning? That was the fourth time that I recorded this episode!
  • I am showing code from 09:25 onward.
  • Fun part that you shouldn’t miss:
    It should fails... It doesn't fails... It should fails... It doesn't fails...
  • From 05:10 to 08:00  - Web Froms Rant (come to think about it, there are a lot of those J )

Am I talking too fast? Am I making any sense?

Errata: in talking about the view engines, there is a section that might looks like I am taking credit for building Boo, just to clarify, I am an avid user of Boo, but I can't claim any credit for it. What I meant was that I built Brail, which uses Boo.

time to read 64 min | 12633 words

While WebForms and MonoRail share the ability to refactor common UI elements into reusable components, the design decisions that should be considered for each of those is quite different. Build view components for MonoRail is much simpler than building controls for web forms, for one :-)

I talked in the past about the mechanics of implementing View Components, now I want to talk about several best practices that may be useful for developers building view components for MonoRail. For this discussion, I am going to give examples from the grid component for MonoRail, whose implementation can be found here and here. In what seems like the traditional manner for MonoRail code, the grid components comes in two flavors, GridComponent, that contains the basic functionality, and SmartGridComponent, that contains a lot more smarts and assumptions. There is nothing particularily interesting in this decision, it is just a way to split the responsabilities in a clear manner.

View Components are all about the UI, and I am going to talk about general components, not something specific for a single page/use-case. A lot of what I want to cover is about allow easy extensability and re-use of the component, which is not an issue if you are building it for a single use case only.

In order to understand view components, we first need to understand a bit about the view engines in MonoRail. The view engines (broadly) have the following concepts:

  • Template / View - will generate the HTML for a request. Similar to an aspx page.
  • Sub template / Sub view - is called from a parent template/view in order to handle some of the processing. Similar to an ascx control. However, a sub view is also a fully fledged view, so you can use it to render a page in one scenario, and embed it in another page in a second scenario (quite useful, by the way).
  • View Component - responsible to handle the rendering of a piece of HTML, similar to a server control. May contains sections (which can be required or optional).
  • Section - Resides inside a view components, allow to pass the view component a template that it can render at will. The best analogy from the web forms world are templates (<ItemTemplate> in a repeater, for instance).

Now that we have covered the concepts, let us move into the implementation. A grid is very common is web applications, and it is a very simple to build. You have a data source, and you simply want to generate the headers and rows tags accordingly.

A rudimentary implementation of that would give us a repeater, which can be as simple as:

public class RepeaterComponent : ViewComponent

{

      public override bool SupportsSection(string name)

      {

            return "item".Equals(name, StringComparison.CurrentCultureIgnoreCase);

      }

 

      public override void Initialize()

      {

            if(Context.HasSection("item")==false)

            {

                  throw new ArgumentException("RepeaterComponent must have an 'item' section");

            }

      }

 

      public override void Render()

      {

            IEnumerable source = ComponentParams["source"] as IEnumerable;

            if (source == null)

                  throw new ArgumentException(
                     
"RepeaterComponent must have an enumerable 'source' parameter.");

            foreach (object item in source)

            {

                  PropertyBag["item"] = item;

                  Context.RenderSection("item");

            }

      }

}

You can use this view component like this:

<%

component RepeaterComponent, {@source: customers}:

      section item:

            output item.CompantName

      end

end

%>

This should give you an idea about how the interaction between the view and the view component works. We pass arguments to the view component in the declaration, and we pass sections in the body. The view component can then access them via the ComponentParams variable and the Context, respectively. Note the foreach inside the Render() method, we assign the the variable "item" to the PropertyBag, which would expose it to the section, and then we render the section, which can now access the "item" variable that we just assigned.

I would never write such a component, because this is much simpler to understand, and does the same thing:

<%

for item in customers:

      output item.CompantName

end

%>

We need the repeater in WebForms, because there is no way to iterate over a parameter, nor easy way to pass data between the markup and the code.

The overall structure of a grid is similar to this:

<table> <!-- table start -->

      <tr><!-- header -->

            <th>Header 1</th>

            <th>Header 2</th>

      </tr>

      <tr><!-- data rows -->

            <td>column 1</td>

            <td>column 2</td>

      </tr>

</table><!-- table end -->

<!-- pagination -->

<b>first</b> | <b>prev</b> | <b>next</b> | <b>last</b>

I am sure that you already know of this, so why am I boring you with this? Well, in order to build a reusable piece of code, you need to allow customization of this basic structure. While you could build a component that would generate everything except the data rows, that would be of very limit use. Off the top of my head, I want to be able to control cell spacing and padding, the border and padding, the table CSS class, etc...

You can pass parameters to the component to control the rendering of the control, and indeed, this is the way that the GridView is using. On last count, the GridView has 85 properties, most of which can be used to control the output of the GridView. Trying to build that is going to take a long time, and it is going to be a very complex task. That doesn't really fit the MonoRail (or Castle in general) philosophy.

The Reflector output of the GridView goes well beyond 3,500 lines of code. I have built full blown systems that had less lines of code than that. (Just to compare, the entire Rhino Mocks code base now stands at about 7,900 Lines.

Let us take a look at the Render() method of the GridComponent, shall we?

public override void Render()

{

      IEnumerable source = ComponentParams["source"] as IEnumerable;

      if (source == null)

      {

            throw new ViewComponentException(

                  "The grid requires an IEnumerable parameter named 'source' ");

      }

 

      ShowStartTable();

      ShowHeader(source);

 

      ShowRows(source);

 

      ShowFooter();

      ShowEndTable();

 

      IPaginatedPage page = source as IPaginatedPage;

      if (page != null)

      {

            ShowPagination(page);

      }

}

We can see that we are following the same structure as above, but with methods name. Remember all the options I wanted to set on the <table> tag alone? How do I handle it with this component? I certainly can't give it up, and I most certainly don't want to start writing > 3,000 lines of code to cover each eventuallity.

Well, it turns out that there is another way to express what I want the <table> tag to look like. Are you ready?  Sure that you are ready?

Well, it turns out that I can expression what I want the <table> tag to look like using a brand new concept called HTML. Here is how I can do this:

<%

component SmartGridComponent, {@source: customers}:

      section tableStart:

%>

      <table cellpadding="2" cellspacing="0" style="border: dashed 2px red">

<%

      end

end

%>

This also shows inline HTML inside a section, but this isn't as important as the concept. We express what we want in the native language. We don't need some translation layer in between. This turn out to simplify quite a bit of my life.

How does the ShowStartTable() method looks like?

private void ShowStartTable()

{

      if (Context.HasSection("tablestart"))

      {

            Context.RenderSection("tablestart");

      }

      else

      {

            RenderText("<table id='grid'>");

      }

}

This approach, sensible defaults with the ease of overriding them, means that it is:

  • Extremely easy to build components (a few examples)
  • Very easy to use components

Gaining Some Smarts

Everything I said so far is possible in WebForms, it is just alien to the way Microsoft positioned WebForms. Now let us move from the realms of the obvious to realm of really cool stuff. We now move from talking about GridComponent to talking about SmartGridComponent. SmartGridComponent inherits from GridComponent and handles a lot of the details of renderring the UI. Mostly, it moves the level that we need to handle from the entire grid to single properties of the object. Think about it like turning AutoGenerateColumns to true in the GridView, except that it is not like that at all ;-)

A View Component is free to define what sections it can support, and there is not compile time limitation. Using this tidbit, we can start doing more interesting things with convention over configuration. Let us say that I want to display a list of customers, and I want to customize the header of the customer id column in some manner. I can do it like this:

<%

component SmartGridComponent, {@source: customers}:

      section customerIdHeader:

            output "<th>Id</th>"

      end

end

%>

By specifying a section with the property name postfixed with "Header", I can override the renderring of this column with my own code. This is a simple example of replacing the text, but I could put anything there at all, including complex HTML or calling a sub view or another view compoennt.

Under the same principal, I can override the rendering of the column itself:

<%

component SmartGridComponent, {@source: customers}:

      section customerId:

            output "<td><b>${item}</b></td>"

      end

end

%>

Again, this just shows the customer id in bold, but anything it possible here. Let us see what we need to do to make this happen, shall we?

Here is a small section from the code that is responsible for rendering the headers of the grid:

foreach (PropertyInfo property in this.properties)

{

      string overrideSection = property.Name + "Header";

      if (Context.HasSection(overrideSection))

      {

            Context.RenderSection(overrideSection);

            continue;

      }

      RenderText("<th class='grid_header'>");

      RenderText(SplitPascalCase(property.Name));

      RenderText("</th>");

}

We check the existance of an overriding section, and defer to it if it exists. The same goes for the rendering the column itself:

foreach (PropertyInfo property in properties)

{

      if (Context.HasSection(property.Name))

      {

            PropertyBag["item"] = property.GetValue(item, null);

            Context.RenderSection(property.Name);

            continue;

      }

      RenderText("<td>");

      object val = property.GetValue(item, null) ?? "";

      RenderText(val.ToString());

      RenderText("</td>");

}

The technical details are very simple, which is very good, but the power and flexibility that they bring is quite amazing.

The combination of convention over configuration, and the ease of overriding the defaults is a key stregth to building complex UI easily. It is important to note that when we actually build the UI, we never really leave the realm of HTML, what we write is very close to what would be sent to the browser. This give a lot more control over the final output, but not at the expense of having to deal with additional complexity.

I would like to end with a quote (via Avery):

Simplicity is about subtracting the obvious, and adding the meaningful.

 

time to read 3 min | 438 words

The technical details are going to be of interest to a limited number of people, but it comes down to a design choice that I made when I first wrote Brail. It has to do with how the text is outputted into the compiler, and some tricky bits that can happen there because Boo's compiler understand that "this is ${code}" and can turn it into the correct string, based on the value of the variable "code".

That was fine until I realized that I can do more than a simple variable substitution in there. You can call anything that returns a value, which is very valuable for code such as:

${Ajax.InstallScripts()}

The problem started when the code inside the expression got even more complicated. No longer a single method call, but method calls that wanted to pass string parameters (with " in the middle), or maybe they wanted to use inline expressions as well. I always work-around those issues, but today I got a piece of code that was too pretty to give up on, so I fixed the Brail pre-parser (thrice, until I got it right) so it would output the correct code to the compiler.

What this basically boils down to is that this is now valid code:

${Html.LinkToAttributed( title ,@customers, @list, {
      @onclick: "paginate(${pageIndex});" 
    }
)}

If you are interested, the output of this is:

<

a href="/customers/list.rails" onclick="paginate(5);">first </a>

I like it.

time to read 14 min | 2702 words

I am preparing to my Dev Teach talks, and I wanted to show a cool view component. Here is a sample of how it looks like now:

<%

component SmartGridComponent, {

      @source : customers,

      @displayAddress: false,

      @displayFax: false,

      @displayPhone: false

}

%>

I want to ignore some of the properties of the customer entity. This demonstrate both symbols in Brail (hmm, sounds like something else altogether :-) ) and using Convention over Configuration for this.

And here is a bit more complex example, showing how to display just the items that we want, in the order that we want:

<%

component SmartGridComponent, {

       @source : customers,

       @columns: [

              @customerId,

              @companyName,

              @contactName,

              @contactTitle

       ]

}

%>

And here is how I can override spesific behavior for the grid:

<%

component SmartGridComponent, {

       @source : customers,

       @columns: [

              @customerId,

              @companyName,

              @contactName,

              @contactTitle

       ]}:

       section customerIdHeader:

              output "<th>Id</th>"

       end

       section customerID:

       %>

       <td>${Html.LinkTo(item, "Customers", "ListOrder", item)}</td>

       <%

       end

end

%>

Here is how the last one looks like, by the way:

(Image from clipboard).png

time to read 21 min | 4179 words

In my last post, I show how to use the Ajax Generators, but I didn't really explain why it is good to learn yet another way to do Ajax. Here is the example that I used:

page.ReplaceHtml('paginatedGrid', {@partial : 'faq/grid' })
page.VisualEffect('Highlight', 'paginatedGrid')

This will send the following to the client:

try 
{
 Element.update("paginatedGrid","gridContent");
 new Effect.Highlight('paginatedGrid', {});
}
catch(e)
{
 alert('JS error ' + e.toString());
 alert('Generated content: \nElement.update(\"paginatedGrid\",\"gridContent\");\nnew Effect.Highlight(\'paginatedGrid\', {});\n');
}

(Replace gridContent with a lot of HTML, of course). Okay, so I get nicer error handling if I have an error (always a good thing), but I don't see the value of it. Let us consider the case where we don't have something as simple as straight rendering to the client, shall we?

A good example will be in a shopping cart, if the total price of the items reach above a certain threshold, the user should get free shipping. Now that we have a conditional, the code isn't this simple to write. Add about 15 lines of JS for each condition, and you are in a world of hurt.

This is where the generators come real handy, you aren't manipulating text, but objects, which means that you get to take advantage of advance programming language constructs like conditionals and looping. Let us see how we can implement the shopping cart functionality. We will start with the logic, which in this case is sitting in the controller (probably not a good idea for non-demo scenarios):

private string[] products = { "Milk", "Honey", "Pie" };

 

public void Index()

{

       PropertyBag["cart"] = Cart;

       PropertyBag["products"] = products;

}

 

public void AddItem(int id)

{

       PropertyBag["alreadyHadfreeShipping"] = Cart.Count > 4;

       Cart.Add(products[id]);

       PropertyBag["produdct"] = products[id];

       PropertyBag["freeShipping"] = Cart.Count > 4;

       RenderView("AddItem.brailjs");

}

 

public void ClearAllItems()

{

       PropertyBag["alreadyHadfreeShipping"] = Cart.Count > 4;

       InitCart();

       RenderView("ClearAllItems.brailjs");

}

Cart is a property that exposed an ArrayList saved to the user session. You can see that we are passing the decisions to the views, to act upon. There is a business rules that says that if you buy more than 4 items you get free shipping, and that is what is going on here. No dealing with the UI at all.

Now, let us see the views, we will start with the main one, index.brail

<?brail import Boo.Lang.Builtins ?>

<h2>Demo shopping cart:</h2>

<ul>

       <?brail for i in range(products.Length):?>

              <li>

                      ${ajax.LinkToRemote(products[i],'addItem.rails?id='+i,{})}

              </li>

       <?brail end ?>

</ul>

<div id="freeShipping" style="display: none;">

       <b>You are elegible for free shipping!</b>

</div>

<h2>Purchased Products:</h2>

<p>

       ${ajax.LinkToRemote('Clear','clearAllItems.rails',{})}

</p>

<ul id="products">

       <?brail for product in cart: ?>

              <li>${product}</li>

       <?brail end ?>

</ul>

The first line shows an interesting trick, by default Brail removes the builtin namespace, because common names such as list and date exist there. Here, I want to use the range function, so I just import it and continue as usual. Beyond that, there is nNothing particulary interesting about this view, I think.

Let us see the addItem.brailjs file first, it handles updating the page to reflect the new item in the cart:

page.InsertHtml('bottom', 'products',"<li>${produdct}</li>")

if freeShipping:

      page.Show('freeShipping')

     

      if not alreadyHadfreeShipping:     

            page.VisualEffect('Highlight','freeShipping')

      end

end

As you can see, it contains conditional logic, but this logic is strictly UI focused. If the user is applicable for free shipping show it, if this is the first time, highlight the fact that they got a free shipping, so they will notice.

I would like to see similar functionality done the other way, but I do not think that I would care to write it...

Using generators gives you code that is highly maintainable in the future, and doesn't move business logic to the UI or UI logic to the business logic like often happen (by neccesaity) using other methods.

time to read 25 min | 4862 words

I mentioned that MonoRail recently aquired Ajax Generators. The generators a are different from normal templates, because they do not generate html, but rather modify the page that was already rendered. It will be easier to exaplain with an example. Let us take the simple example of paging a grid without a full post back, shall we?  By the way, this turned out to be a lot more focused at the WebForms than I intended, more on that later.

Here is the backend of this demo:

public void Index(bool isAjax)

{

       PropertyBag["subjects"] = PaginationHelper.CreateCachedPagination(

              this, Action,15,delegate

              {

                     return new List<Subject>(Repository<Subject>.FindAll()).ToArray();

              });

       if(isAjax)

              RenderView("index.brailjs");

}

Simple, get the items from the database and store it in the cache, as well as passing it to the view. For now, please ignore the last two lines...

Now, let us a look at our view, shall we? There is a layout that I will not touch now (if you don't understand MonoRail terms, think about it as a MasterPage), but let us focus on the action's view index.brail:

<script type='text/javascript'>

       function error(e)

       {

              alert(e);

       }

       function paging(index)

      {

           var url = '/faq/index.rails';

           var pars = 'page=' + index +'&isAjax=true';       

           new Ajax.Request(url,{method: 'get', evalScripts: true, parameters: pars, onException: error});       

   

       }

</script>

<h2>Subjects for questions:</h2>

<div id='paginatedGrid'>

       <?brail OutputSubView('grid') ?>

</div>

What we have here is merely a bit of javascript and a div to put the gird in. We also use OutputSubView for the grid (again, if WebForms terms, this is something like a UserControl). The most complex part is the grid.brail view itself:

<?brail

component GridComponent, {'source':subjects}:

       section header:

?>

       <th id='header'>Id</th>

       <th id='header'>Name</th>

       <th id='header'>Browse</th>

       <?brail

       end

       section item:

?>

       <tr id='item'>

              <td>${item.Id}</td>

              <td>${item.Name}</td>

              <td>${ HtmlHelper.LinkTo('Browse','faq','showQuestions', item.Id) }</td>

       </tr>

<?brail

       end

       section alternateItem:

?>

       <tr id='alternateItem'>

              <td>${item.Id}</td>

              <td>${item.Name}</td>

              <td>${ HtmlHelper.LinkTo('Browse','faq','showQuestions', item.Id) }</td>

       </tr>

 <?brail

       end

       section link:

?>

       <a href='/faq/index.rails?page=${pageIndex}'

          onclick='paging(${pageIndex});return false;'>${title}</a>

       <?brail

       end

end

 ?>

The GridComponent is somewhere in the middle between GridView and Repeater, since it can do almost everything on its own, but let you override (for isntnace, alternateItem) parts of the rendering in place. Please pay some attention to the last section, the link. This is a pagination link that can be used to override the default behavior of moving to the next page. I am overriding it with a

Now that we have established the page, we can play with it a bit, and see that it is working great. Except that there is still the last piece of the puzzle, paging without refreshing the full page.

Go check the javascript in index.brail, notice that it is making an Ajax request to /faq/index.rails ? And that it is passing isAjax=true? No go and check the Index() method, and look at the last two lines. If this is an Ajax request, we use a different view for the output, index.brailjs;

page.ReplaceHtml('paginatedGrid', {@partial : 'faq/grid' })
page.VisualEffect('Highlight', 'paginatedGrid')

Now, what is going on here... this certianly doesn't look like any ajax framework I have ever seen...

Remember that I spoke about the ajax generators? This is it. You may think about is as a small DSL for generating the javascript to modify the page. What we have here is a call to ReplaceHtml, which will replace the content of the paginatedGrid element with something else, usually a string. But, in this case, we pass in a @partial (@symbol is identical to :symbol in Ruby, but this is a temporary syntax at the moment, I want to get rid of the {} ), which means that we ask to replace the element with the results of running the grid template.  The second line is just to provide some feedback to the user that something has changed. So, in essense, it took 4 lines of code to

But why invent a whole new syntax just to use ajax? Isn't Javascript enough? More on that next post...

time to read 5 min | 943 words

I call it a "new" feature because it is something that the NVelocity view engine supports for quite some time. The basic idea is to allow this type of code:

<?brail

component BasicGridComponent, {"source": contacts}:

  section header:

?>

    <th>EMail</th>

    <th>Phone</th>

<?brail

  end

  section item:

?>

    <td>${item.Email}</td>

    <td>${item.Phone}</td>

<?brail

  end

end

?>

This feature open up some very interesting possibilities with View Components...

FUTURE POSTS

No future posts left, oh my!

RECENT SERIES

  1. API Design (10):
    29 Jan 2026 - Don't try to guess
  2. Recording (20):
    05 Dec 2025 - Build AI that understands your business
  3. Webinar (8):
    16 Sep 2025 - Building AI Agents in RavenDB
  4. RavenDB 7.1 (7):
    11 Jul 2025 - The Gen AI release
  5. Production postmorterm (2):
    11 Jun 2025 - The rookie server's untimely promotion
View all series

Syndication

Main feed ... ...
Comments feed   ... ...