Single document site

When the website consists of static data, it is possible to save all data into a single MongoDB document. This way, it is possible to define hundreds of small sites on a single rails instance almost instantly with as little overhead as possible. It also shouldn't be too hard to implement caching logic and thus get very fast responses for such system.

 

dc_process_default_request action will of course not be adequate for processing web request, so we need to create a new process default request action and point *path route to it. DRG CMS already provides dc_single_sitedoc_request action for this purpose. If you choose to use it, replace *path route in routes.rb with this line:

 

get '*path' => 'dc_application_controller#dc_single_sitedoc_request'

 

The action includes the following assumptions:

  • all data is saved into dc_site document
  • design must also be defined in dc_site document
  • web page data is saved into dc_parts embedded documents
  • value ''document'' in div_id field of dc_part document indicates documents that form site menu
  • name field of dc_part defines menu link caption
  • link field of dc_part defines site page link
  • dc_part_renderer.rb defines two render methods required to render web page data. single_sitedoc_menu will render web site menu and single_sitedoc will render page data.

Action source code:

def dc_single_sitedoc_request
  session[:edit_mode] ||= 0
  @site = dc_get_site
  # @site is not defined. render 404 error
  return dc_render_404('Site!') unless @site

  dc_set_options(@site.settings)
  # HOMEPAGE. When no parameters is set
  params[:path] = @site.homepage_link if params[:path].nil? 
  @parts = @site.dc_parts
  @part  = @parts.find_by(link: params[:path])
  return dc_render_404('Part!') unless @part

  @page_title = "#{@site.page_title} #{@part.name}"
  @js, @css = '', ''
  layout = session[:edit_mode] > 0 ? 'cms' : @site.site_layout

  if @site.rails_view.blank?
    design = '<%= dc_page_top %>' + @site.design + '<%= dc_page_bottom %>'
    render(inline: design, layout: layout)
  else
    render @site.rails_view, layout: layout
  end
end

 

Action code is a striped down version of default request process code. The program searches for site document and returns an error if the site document is not found. After that, settings are initialized. If path is not set, homepage document link is determined by homepage_link field. 

 

After that, internal parts are assigned to site dc_parts documents and finally a document with link defined by path parameter is searched. Again if not found error is returned.

 

After that, page_title is defined, JavaScript and CSS variables are initialized, and Rails layout is determined. With all this data, it's time to render Rails view. If view is defined as design code, some additional code is added and design is rendered inline. If a view is defined in a file, the Rails view file is rendered.

 

Design code, which doesn't require explanation:

<div id='page'>
 <div id="site-top">
   <a href="http://www.mysite.com/"><img alt="logo" src="/files/logo.jpg"></a>
   <div id="site-name">Site name</div>
 </div>

 <%= dc_render(:dc_part, method: 'single_sitedoc_menu', menu_div: 'menu-name') %>

 <div id="body"><%= dc_render(:dc_part, method: 'single_sitedoc') %></div>
 <div id="footer"><%= dc_render(:dc_part, method: 'single_sitedoc', div_id: 'footer') %></div>
</div>

 

dc_part_renderer.rb contains two render methods which are used to render web page data. 

 

First, single_sitedoc method, is used to render page data content:
 

def single_sitedoc
  # if div_id option specified search for part with this div_id.
  # This can be used to render footer or header of site.
  part = if @opts[:div_id]
    @parent.parts.find_by(div_id: @opts[:div_id])
  else
    @parent.part
  end
  # part not found. Render error message.
  return "Part #{@opts[:div_id]} not found!" if part.nil?

  # prepare edit parameters 
  @opts[:editparams].merge!(id: part, ids: @parent.site._id, formname: 'dc_part',
                            table: "dc_site;dc_part", record_div_id: 'document' )
  render_particle(part, @opts)
end

 

Second, single_sitedoc_menu method, is used to render web site menu:
 

def single_sitedoc_menu
  # prepare div markup
  menu_div = @opts[:menu_div] ? "id=#{@opts[:menu_div]}" : ''
  html = "<div #{menu_div}><ul>\n"
  # collect all dc_part documents which make menu
  @parent.parts.where(div_id: 'document').order_by(order: 1).each do |part|
    # mark selected item   
    selected = (part == @parent.part) ? 'class="menu-selected"' : ''
    html << "<li #{selected}>#{ @parent.link_to(part.name, part.link ) }</li>\n"
  end
  html << "</ul></div>\n"
end

 

As you can see, It is easy to create your own request process action controller and render data on a web page. You are not bound by models (collections) defined in DRG CMS. Go ahead and define your own models and use them for saving your website data and enjoy simplicity of entering data with DRG Forms. 

 


Last update: 26.01.2022