Class: SectionRenderer
- Inherits:
-
Object
- Object
- SectionRenderer
- Includes:
- ActionView::Helpers::TagHelper
- Defined in:
- app/presentation/section_renderer.rb
Overview
The Section Architecture
Umlaut has what could be considered a ‘domain specific language’ for describing the display individual sections of content on the resolve menu page. These sections often correspond to a ServiceTypeValue, like “fulltext”. But sometimes may include multiple ServiceTypeValues (eg related_items section includes cited_by and similar_items), or no ServiceTypeValue at all (eg section to display a COinS).
A description of a section is simply a hash with certain conventional keys describing various aspects of the contents and display of that section. These hashes are listed in the resolve_sections application configuration variable, initialized in the resolve_views.rb initializer, and customized or over-ridden in the local resolve_views.rb initializer.
One benefit of describing a section through configuration is that section display can often by changed at configure time without requiring a code time. Another is that the description of the section can be used not only to generate the initial HTML page; but also by the javascript that update the sections with new background content as available; and by the partial_html_sections api that delivers HTML fragments for sections in an XML or JSON container.
A description of a section is simply a hash, suitable for passing to SectionRenderer.new, detailed below. Plus some additional variables specifying where to display the section, documented in the resolve_views.rb initializer.
The SectionRenderer
A SectionRenderer object provides logic for displaying a specific section on the Umlaut resolve menu page. It is initialized with a hash describing the details – or significantly, with simply a pointer to such a hash already existing in the resolve_sections config variable.
A SectionRenderer is typically created by the ResolveHelper#render_section method, which then passes the SectionRender object to the _section_display.erb.html that does the actual rendering, using the SectionRenderer for logic and hashes to pass to render calls in the partial.
Section Options
Section options are typically configured in hashes in the application config variable resolve_sections, which is expected to be a list of hashes. That hash is suitable to be passed to a SectionRenderer.new() as configuration options for the section. The various ways these options can be used is documented below.
Simplest Case, Defaults
As is common in ruby, SectionRenderer will make a lot of conventional assumptions, allowing you to be very concise for the basic simple case:
{ :div_id => "fulltext", :html_area => :main }
This means that:
-
this section is assumed to be contained within a <div id=“fulltext”>. The div won’t be automatically rendered, it’s the containing pages responsibility to put in a div with this id.
-
this section is assumed to contain responses of type ServiceTypeValue
-
The section will be displayed with stock heading block including a title from Rails i18n under key ‘umlaut.display_sections.#section_id.title`, or if not there then constructed from the display_name of ServiceTypeValue, or in general the display_name of the first ServiceTypeValue included in this section.
-
The section will include a stock ‘spinner’ if there are potential background results being gathered for the ServiceTypeValue(s) contained.
-
The actual ServiceResponses collected for the ServiceTypeValue included will be rendered with a _standard_response_item partial, using render :collection.
-
The section will be displayed whether or not there are any actual responses included. If there are no responses, a message will be displayed to that effect.
The display of a section can be customized via configuration parameters to a large degree, including supplying your own partial to take over almost all display of the section.
Customizing ServiceTypeValues
You can specifically supply the ServiceTypeValues contained in this section, to a different type than would be guessed from the div_id:
{:div_id => "my_area", :service_type_values => ["fulltext"]}
Or specify multiple types included in one section:
{:div_id => "related_items", :service_type_values => ['cited_by', 'similar]}
Or a section that isn’t used for displaying service responses at all, and has no service type:
{:div_id => "coins", :partial => "coins", :service_type_values => []}
Note that a custom partial needs to be supplied if there are no service_type_values supplied.
Customizing heading display
Using Rails i18n, you can supply a title for the section that’s different than what would be guessed from it’s ServiceTypeValues. You can also supply a prompt.
{:div_id =>"excerpts"}
In your config/locales/en.yml (or other language):
umlaut:
display_sections:
excerpts:
title: "Really great Excerpts"
prompt: "Click on them to see them, far out!"
You can also suppress display of the stock section heading at all:
{:show_heading => false, ...}
This may be becuase you don’t want a heading, or because you are supplying a custom partial that will take care of the heading in a custom way.
Customizing spinner display
You can also suppress display of the stock spinner, because you don’t want a spinner, or because your custom partial will be taking care of it.
{:show_spinner => false, ...}
By default, the spinner displays what type of thing it’s waiting on, guessing from the ServiceTypeValue configured. If you want to specify this item name, use Rails i18n under the section_id in config/locales/en.yml, generally using a plural name:
umlaut:
display_sections:
excerpts:
load_more_item_name: "amazing excerpts"
Customizing visibility of section
By default, a section will simply be displayed regardless of whether there are any actual responses to display. However, the ‘visibility’ argument can be used to customize this in many ways. visibilty:
- true
-
Default, always show section.
- false
-
Never show section. (Not sure why you’d want this).
- :any_services
-
Show section if and only if there are any configured services that generate the ServiceTypeValues included in this section, regardless of whether in this case they have or not.
- :in_progress
-
Show the section if responses exist, OR if any services are currently in progress that are capable of generating responses of the right type for this section.
- :responses_exist
-
Show the section if and only if some responses have actually been generated of the types contained in this section.
- :complete_with_responses
-
Show the section only if there are responses generated, AND all services supplying responses of the type contained in section have completed, no more responses are possible.
- (lambda object)
-
Most flexibly of all, you can supply your own lambda supplying custom logic to determine whether to show the section, based on current context. The lambda will be passed the SectionRenderer object as an argument, providing access to the Umlaut Request with context. eg:
:visibility => lambda do |renderer| renderer.request.something == something end
List with limit
You can have the section automatically use the ResolveHelper#list_with_limit helper to limit the number of items initially displayed, with the rest behind a ‘more’ expand/contract widget.
{ :div_id => "highlighted_link",
:list_visible_limit => 1,
:visibility => :in_progress, ... }
Custom partial display
By default, the SectionRenderer assumes that all the ServiceResposnes included are capable of being displayed by the standard_item_response, and displays them simply by render standard_item_response with a :colection. Sometimes this assumption isn’t true, or you want custom display for other reasons. You can supply your own partial that the renderer will use to display the content.
{ :div_id => "my_div", :partial => "my_partial", ... }
The partial so supplied should live in resolve/_my_partial.html.erb
When this partial is called, it will have local variables set to give it the data it needs in order to create a display:
- responses_by_type
-
a hash keyed by ServiceTypeValue name, with the the value being an array of the respective ServiceType objects.
- responses
-
a flattened list of all ServiceTypes included in this section, of varying ServiceTypeValues. Most useful when the section only includes one ServiceTypeValue
- renderer
-
The SectionRenderer object itself, from which the current umlaut request can be obtained, among other things.
You can supply additional static local arguments to the partial in the SectionRenderer setup:
{:div_id=> "foo", :partial=>"my_partial", :partial_locals => {:mode => "big"}, ... }
the :partial_locals argument can be used with the standard_response_item too:
{:div_id => "highlighted_link", :partial_locals => {:show_source => true}}
Note that your custom partial will still be displayed with stock header and possibly spinner surrounding it. You can suppress these elements:
{:div_id => "cover_image", :partial => "cover_image", :show_heading => false, :show_spinner => false}
But even so, some ‘wrapping’ html is rendered surrounding your partial. If you want to disable even this, becuase your partial will take care of it itself, you can do so with :show_partial_only => true
{:div_id => "search_inside", :partial => "search_inside", :show_partial_only => true}
Instance Method Summary collapse
-
#any_services? ⇒ Boolean
do any services exist which even potentially generate our types, even if they’ve completed without doing so?.
-
#content_render_hash ⇒ Object
A hash suitable to be passed to Rails render() to render the inner content portion of the section.
- #custom_partial? ⇒ Boolean
- #div_id ⇒ Object
-
#initialize(a_umlaut_request, section_def = {}) ⇒ SectionRenderer
constructor
First argument is the current umlaut Request object.
-
#item_render_hash(item) ⇒ Object
used only with with list_with_limit functionality in section_display partial.
- #list_visible_limit ⇒ Object
- #request ⇒ Object
-
#responses ⇒ Object
Hash of ServiceType objects (join obj representing individual reponse data) included in this section.
- #responses_empty? ⇒ Boolean
-
#responses_list ⇒ Object
All the values from #responses, flattened into a simple Array.
-
#section_etag ⇒ Object
For a given resonse type section, returns a string that will change if the rendered HTML has changed, HTTP etag style.
-
#section_prompt ⇒ Object
Optional section prompt, from Rails i18n key ‘umlaut.display_sections.#section_div_id.prompt` Deprecated legacy, you can force with :section_prompt key in section config hash.
-
#section_title ⇒ Object
Display title of the section can come from several places, in order of precedence: * 1.
-
#service_type_values ⇒ Object
Returns all ServiceTypeValue objects contained in this section, as configured.
-
#services_in_progress? ⇒ Boolean
Whether any services that generate #service_type_values are currently in progress.
- #show_heading? ⇒ Boolean
- #show_partial_only? ⇒ Boolean
- #show_spinner? ⇒ Boolean
-
#spinner_render_hash ⇒ Object
A hash suitable to be passed to Rails render(), to render a spinner for this section.
-
#visible? ⇒ Boolean
Is the section visible according to it’s settings calculated in current context?.
Constructor Details
#initialize(a_umlaut_request, section_def = {}) ⇒ SectionRenderer
First argument is the current umlaut Request object. Second argument is a session description hash. See class overview for an overview. Recognized keys of session description hash:
- id
-
SessionRenderer will look up session description hash in resolve_views finding one with :div_id == id
- div_id
-
The id of the <div> the section lives in. Also used generally as unique ID for the section.
- service_type_values
-
ServiceTypeValue’s that this section contains. defaults to [ServiceTypeValue]
- section_title
-
(DEPRECATED, use Rails i18n) Title for the section. Defaults to service_type_values.first.display_name
- section_prompt
-
Prompt. Default nil.
- show_heading
-
Show the heading section at all. Default true.
- show_spinner
-
Show a stock spinner for bg action for service_type_values. default true.
- visibilty
-
What logic to use to decide whether to show the section at all. true|false|:any_services|:in_progress|:responses_exist|:complete_with_responses|(lambda object)
- list_visible_limit
-
Use list_with_limit to limit initially displayed items to value. Default nil, meaning don’t use list_with_limit.
- partial
-
Use a custom partial to display this section, instead of using render(“standard_response_item”, :collection => [all responses]) as default.
- show_partial_only
-
Display custom partial without any of the usual standardized wrapping HTML. Custom partial will take care of it itself.
265 266 267 268 269 270 271 272 273 274 |
# File 'app/presentation/section_renderer.rb', line 265 def initialize(a_umlaut_request, section_def = {}) @umlaut_request = a_umlaut_request @div_id = section_def[:div_id] || section_def[:id] raise Exception.new("SectionRenderer needs a :div_id passed in arguments hash") unless @div_id # Merge in default arguments for this section from config. (section_def) end |
Instance Method Details
#any_services? ⇒ Boolean
do any services exist which even potentially generate our types, even if they’ve completed without doing so?.
414 415 416 417 418 |
# File 'app/presentation/section_renderer.rb', line 414 def any_services? nil != @umlaut_request.dispatched_services.to_a.find do |ds| ! (service_type_values & ds.can_generate_service_types ).empty? end end |
#content_render_hash ⇒ Object
A hash suitable to be passed to Rails render() to render the inner content portion of the section. Called by the section_display partial, nobody else should need to call this. You may be looking for ResolveHelper#render_section instead.
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 |
# File 'app/presentation/section_renderer.rb', line 362 def content_render_hash if custom_partial? {:partial => @options[:partial].to_s, :object => responses_list, :locals => @options[:partial_locals].merge( {:responses_by_type => responses, :responses => responses_list, :umlaut_request => request, :renderer => self})} else {:partial => @options[:item_partial].to_s, :collection => responses_list, :locals => @options[:partial_locals].clone} end end |
#custom_partial? ⇒ Boolean
354 355 356 |
# File 'app/presentation/section_renderer.rb', line 354 def custom_partial? ! @options[:partial].nil? end |
#div_id ⇒ Object
320 321 322 |
# File 'app/presentation/section_renderer.rb', line 320 def div_id return @div_id end |
#item_render_hash(item) ⇒ Object
used only with with list_with_limit functionality in section_display partial.
380 381 382 383 384 385 386 |
# File 'app/presentation/section_renderer.rb', line 380 def item_render_hash(item) # need to clone @options[:partial_locals], because # Rails will modify it to add the 'object' to it. Bah! {:partial => @options[:item_partial], :object => item, :locals => @options[:partial_locals].clone} end |
#list_visible_limit ⇒ Object
420 421 422 |
# File 'app/presentation/section_renderer.rb', line 420 def list_visible_limit @options[:list_visible_limit] end |
#request ⇒ Object
316 317 318 |
# File 'app/presentation/section_renderer.rb', line 316 def request return @umlaut_request end |
#responses ⇒ Object
Hash of ServiceType objects (join obj representing individual reponse data) included in this section. Keyed by string ServiceTypeValue id, value is array of ServiceTypes
297 298 299 300 301 302 303 304 305 |
# File 'app/presentation/section_renderer.rb', line 297 def responses unless (@responses) @responses = {} service_type_values.each do |st| @responses[st.name] = @umlaut_request.get_service_type(st) end end @responses end |
#responses_empty? ⇒ Boolean
312 313 314 |
# File 'app/presentation/section_renderer.rb', line 312 def responses_empty? responses_list.empty? end |
#responses_list ⇒ Object
All the values from #responses, flattened into a simple Array.
308 309 310 |
# File 'app/presentation/section_renderer.rb', line 308 def responses_list responses.values.flatten end |
#section_etag ⇒ Object
For a given resonse type section, returns a string that will change if the rendered HTML has changed, HTTP etag style.
Output in API responses, used by partial html updater to know if a section needs to be updated on page.
tag is created by appending:
-
the progress status for the section (in progress or not)
-
The current visibility status of the section
-
Number of responses in section
-
the timestamp of most recent response in section, if any.
480 481 482 483 484 485 486 487 488 489 490 |
# File 'app/presentation/section_renderer.rb', line 480 def section_etag parts = [] parts << self.services_in_progress?.to_s parts << self.visible?.to_s parts << responses_list.length max = responses_list.max_by {|response| response.created_at} parts << (max ? max.created_at.utc.strftime("%Y-%m-%d-%H:%M:%S") : "") return parts.join("_") end |
#section_prompt ⇒ Object
Optional section prompt, from Rails i18n key ‘umlaut.display_sections.#section_div_id.prompt` Deprecated legacy, you can force with :section_prompt key in section config hash.
456 457 458 459 460 461 462 463 464 465 466 467 |
# File 'app/presentation/section_renderer.rb', line 456 def section_prompt prompt = nil if @options.has_key?(:section_prompt) prompt = @options[:section_prompt] else prompt = I18n.t("prompt", :scope => "umlaut.display_sections.#{self.div_id}", :default => "") end prompt = nil if prompt.blank? return prompt end |
#section_title ⇒ Object
Display title of the section can come from several places, in order of precedence:
* 1. (DEPRECATED) :section_title key in config hash. Prefer i18n instead.
* 2. Rails i18n, under key 'umlaut.display_sections.#{section_id}.title'
* 3. If not given, as a default we use the display_name of the first ServiceTypeValue
object included in this section's results.
If still blank after all those lookups, then no section title. Set a section title
to the empty string in i18n to force no section title.
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 |
# File 'app/presentation/section_renderer.rb', line 432 def section_title section_title = nil if @options.has_key? :section_title # deprecation warning? Not sure the right way to do that. section_title = @options[:section_title] else section_title = I18n.t("title", :scope => "umlaut.display_sections.#{self.div_id}", :default => Proc.new { # Look up from service_type name if possible as default if (service_type_values.length > 0) service_type_values.first.display_name_pluralize.titlecase else "" end }) end section_title = nil if section_title.blank? return section_title end |
#service_type_values ⇒ Object
Returns all ServiceTypeValue objects contained in this section, as configured. Lazy caches result for perfomance.
278 279 280 281 282 283 |
# File 'app/presentation/section_renderer.rb', line 278 def service_type_values @service_type_values ||= @options[:service_type_values].collect do |s| s.kind_of?(ServiceTypeValue)? s : ServiceTypeValue[s] end end |
#services_in_progress? ⇒ Boolean
Whether any services that generate #service_type_values are currently in progress. Lazy caches result for efficiency.
287 288 289 290 291 |
# File 'app/presentation/section_renderer.rb', line 287 def services_in_progress? # cache for efficiency @services_in_progress ||= @umlaut_request.service_types_in_progress?(service_type_values) end |
#show_heading? ⇒ Boolean
324 325 326 |
# File 'app/presentation/section_renderer.rb', line 324 def show_heading? (! show_partial_only?) && @options[:show_heading] end |
#show_partial_only? ⇒ Boolean
350 351 352 |
# File 'app/presentation/section_renderer.rb', line 350 def show_partial_only? @options[:show_partial_only] end |
#show_spinner? ⇒ Boolean
329 330 331 332 |
# File 'app/presentation/section_renderer.rb', line 329 def show_spinner? (! show_partial_only?) && @options[:show_spinner] && @umlaut_request.service_types_in_progress?(service_type_values) end |
#spinner_render_hash ⇒ Object
A hash suitable to be passed to Rails render(), to render a spinner for this section. Called by section_display partial, nobody else should need to call it.
337 338 339 340 341 342 343 344 345 346 347 348 |
# File 'app/presentation/section_renderer.rb', line 337 def spinner_render_hash custom_item_name = I18n.t("load_more_item_name", :scope => "umlaut.display_sections.#{self.div_id}", :default => "") custom_item_name = nil if custom_item_name.blank? { :partial => "background_progress", :locals =>{ :svc_types => service_type_values, :div_id => "progress_#{self.div_id}", :current_set_empty => responses_empty?, :item_name => custom_item_name } } end |
#visible? ⇒ Boolean
Is the section visible according to it’s settings calculated in current context?
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 |
# File 'app/presentation/section_renderer.rb', line 390 def visible? case @options[:visibility] when true, false @options[:visibility] when :any_services any_services? when :in_progress # Do we have any of our types generated, or any services in progress # that might generate them? (! responses_empty?) || services_in_progress? when :responses_exist # Have any responses of our type actually been generated? ! responses_empty? when :complete_with_responses (! responses.empty?) && ! (services_in_progress?) when Proc # It's a lambda, which takes this SectionRenderer as an arg @options[:visibility].call(self) else true end end |