Rendering to Objects

Ticket

+commit
 

This idea is in support of Json rendering.  In Rails after 2.3, the renderer can take an object representation of the final json,  In fact, you can pass it any object and it will call .as_json on the object first, then to_json on the results of that.  What we need is for the renderer to produce Objects instead of strings, objects for to be rendered: render :json=>data_object, :format=>'json', and similar for Xml.

 

I have a branch to investigate the concept.  This is an opportunity to refactory WikiContent which is designed to render and Strings, with ObjectContent that represents the content as parsed into "Chunks", then there is flexibility to serialize directly, or to defer it to the render stage.

 

Wagn processes content for inclusions, links, URLs and any other content that needs to be processed during rendering.   Below is an image showing the classes involved in the current implementation.  The renderers use WikiContent which inherits from String, so the render code creates a WikiContent from the raw content in the card database, and uses that object to create a rendered string for the card to mix with the other content of the cards context.

 

+WikiContent Diagram

Rendering to Objects+solution+Chunks+WikiContent Diagram

 

All of this would be pretty straigthforward except for one special case, the transcluded chunks.  This is also where some of Wagn's power  is implemented, the feature of including the contents of another card when rendering a base card, and then applying this recursively.  The inclusion is contextual, so the renderer needs to supply a block to render!, but only the case of this inclusion feature  in Chunk::Tranclude  actually yeilds to the block.  This occurs in the chunk method unmask_text.

 

Because the final result will be expanded into a single string, the ChunkManager masks the chunks internally when in parses out the chunks on initialization.  It goes through each type of chunk, and turns the source syntax into chunk:id:classchunk where :id is a number assigned to the chunk and :class is the string name of the type.

 

The method render! turns these masked chunks into the rendered forms.  Originally Wagn had only one renderer, so the unmasking would be one form for links, but with new renderers, the link unmasking calls methods that the different renderers can override

 

 

Enter Object Rendering

 

This all works very well until you try to render to json.  The Rails level render really prefers that you give it a ruby object, that it will process like this: @object.as_json.to_json

When you render as: render :json=>@object, :format=> ...

 

The method as_json has a default implementation supplied by Rails, which is nice because it will give you something, but it will probably give you way more than you wanted, so  you implement your own as_json for any classes you need to render.  So, lets move to the next diagram with a new way of producing content as Objects instead of Strings.  It will share the chunk types from the above WikiContent classes.  Some of the internal APIs will change a little, but all the regular expressions are shared so we are implementing the same syntax.

 

Rendering to Objects+solution+ObjectRendering+ObjectContent

 

Since we are working with objects, we can retain the original nesting and structure until final serialization in to_json, and we don't have to mask the chunks, we just turn them into Chunk objects.  The investigative branch linked above (gerryg/wagn - obj_render) adds a second kinds of Content that is used by renderer as an alternative to the original WikiContent class which is still used for the Html and base renderer classes.  This make this implementation more stable, but in principle we can and probably will refactor the WikiContent usage out of the code.  There are some additional parts to doing that as the tracking of wiki refererences is supported in part by WikiContent.  We want to make a clearer seperation of the reference tracking :Refactor Reference Tracking.

 

We add a new class method t the ChunkManager, split_content.  Instead of calling init_chunkmanager after initializing the WikiContent object, ObjectContent#new uses this method to iterate through the chunks and strings in the content producing an Array of String and Chunk typed objects (these objects will be one of the ACTIVE_CHUNKS classes), and stores this array as the value of the new ObjectContent (@obj attribute).  The ObjectContent#as_json method just returns @obj.as_json, which will end up calling .as_json on each Chunk and String.

 

Currently on the branch, Chunk::Abstract#as_json is like this:

def as_json(options={})
    @unmask_text || @unmask_render|| "not rendered #{card and card.name}"
end

Most of the chunk types set @unmask_text at initialization, so they will render as that string always, the yield in Chunk::Transclude#unmask_text return value is  saved to @unmask_render, and @unmask_text will be unset for this case, so we will get what it was in the last ObjectContent#render!   Probably we can refactor away from using render!, but then we would have to supply a block to Chunk::Transclude#as_json because the transcusions would need to be expanded at this point.  The "not rendered..." is debugging, one of them should be set when we call this.

 

 

Card.all.inject({}) {|h,c| j=Wagn::Renderer::JsonRenderer.new(c).render[:card]; puts h[j[:attr][:key]]=j,"\n"; h}

 

That loads up a hash of all the cards in a Wagn.

 

If you do a .to_json on it, you can serialize it to a file or for web export.  All we need to do is tighten up the spec on the card attributes in json.

 

 

This works on obj_render branch.


I'm a little too bleary eyed to dive all the way in right now (rough night with the baby last night), but am stoked to see this taking shape.

  --Ethan McCutchen.....2012-11-20 22:32:47 +0000


We factored out the two Content processors and everything uses ObjectContent, for a string, we use .to_s

  --Gerry Gleason.....2013-03-02 12:20:40 +0000