SysMO-DB/seek

View on GitHub
app/helpers/fancy_multiselect_helper.rb

Summary

Maintainability
A
2 hrs
Test Coverage
module FancyMultiselectHelper
  #the point of this module is to decompose the 'fancy_multiselect' method which was becoming too large and complex.
  #This breaks it into submodules by feature.
  module Base
    def fancy_multiselect object, association, options = {}
      render :partial => 'assets/fancy_multiselect', :locals => options
    end
  end

  module AjaxPreview
    def fancy_multiselect object, association, options = {}
      #skip if preview is disabled
      return super if options.delete :preview_disabled

      #skip if controller class does not define a preview method
      begin
        const = "#{association.to_s.classify.pluralize}Controller".constantize
        return super unless const.method_defined?(:preview)
      rescue NameError=>e
        return super
      end

      #adds options to the dropdown used to select items to add to the multiselect.
      options[:possibilities_options] = {} unless options[:possibilities_options]
      onchange = options[:possibilities_options][:onchange] || ''
      onchange += remote_function(
          :method=>:get,
          :url=>{:action=>"preview", :controller=>"#{association}", :element=>"#{association}_preview"},
          :with=>"'id='+this.value",
          :before=>"show_ajax_loader('#{association}_preview')") + ';'

      options[:possibilities_options][:onchange] = onchange.html_safe
      options = options.reverse_merge :html_options => "style='float:left; width:66%' ".html_safe

      #after rendering the multiselect, throw in a preview box.
      super(object, association, options) + "\n".html_safe + render(:partial => 'assets/preview_box', :locals => {:preview_name => association.to_s.underscore})
    end
  end

  module HideButtonWhenDefaultIsSelected
    def fancy_multiselect object, association, options = {}
      options[:possibilities_options] = {} unless options[:possibilities_options]
      onchange = options[:possibilities_options][:onchange] || ''
      collection_id = options[:name].to_s.gsub(']','').gsub(/[^-a-zA-Z0-9:.]/, "_")
      possibilities_collection_id = "possible_#{collection_id}"
      assign_Selected_Item_js = "addSelectedToFancy('#{collection_id}', $F('#{possibilities_collection_id}'));".html_safe
          onchange += assign_Selected_Item_js
      options[:possibilities_options][:onchange] = onchange.html_safe
      super(object, association, options)
    end
  end

  module OtherProjectsCheckbox
    def fancy_multiselect object, association, options = {}
      if options[:project_possibilities]  &&  options[:other_projects_checkbox]
        type = object.class.name.underscore
        default_checked = Seek::Config.is_virtualliver ? true : false
        check_box_and_alternative_list = "".html_safe
        check_box_and_alternative_list << "<br/>".html_safe
        check_box_and_alternative_list <<  check_box_tag("include_other_project_#{association}", nil, default_checked, {:onchange => "swapSelectListContents('possible_#{type}_#{association.to_s.singularize}_ids','alternative_#{association.to_s.singularize}_ids');", :style => "margin-top:0.5em;"}).html_safe
        check_box_and_alternative_list <<  "Associate #{association.to_s.singularize.humanize.pluralize} from other #{t('project').pluralize}?".html_safe
        check_box_and_alternative_list << select_tag("alternative_#{association.to_s.singularize}_ids",
                                              options_for_select([["Select #{association.to_s.singularize.humanize} ...", 0]]|options[:project_possibilities].collect { |o| [truncate(h(o.title), :length => 120), o.id] }), {:style => 'display:none;'}).html_safe

        options[:association_step_content] = '' unless options[:association_step_content]
        options[:association_step_content] = (options[:association_step_content] + check_box_and_alternative_list).html_safe


        swap_project_possibilities_into_dropdown_js = "<script type='text/javascript'>
                                                        var include_other_projects_checkbox = document.getElementById('include_other_project_#{association}');
                                                        if (include_other_projects_checkbox.checked == false){
                                                        swapSelectListContents('possible_#{type}_#{association.to_s.singularize}_ids','alternative_#{association.to_s.singularize}_ids');
                                                        }
                                                       </script>".html_safe

        super + swap_project_possibilities_into_dropdown_js
      else
        super
      end
    end
  end

  module SetDefaults
    def fancy_multiselect object, association, options = {}
      options[:object_type_text] = options[:object_class].name.underscore.humanize unless options[:object_type_text]
      with_new_link = options[:with_new_link] || false
      object_type_text = options[:object_type_text]
      object_type_text = (I18n.t 'biosamples.sample_parent_term').capitalize if object_type_text == 'Specimen'

      #set default values for locals being sent to the partial
      #override default values with options passed in to the method
      options.reverse_merge! :intro => "The following #{association.to_s.singularize.humanize.pluralize.downcase} are associated with this #{object_type_text.downcase}:",
                             :button_text => "Associate with this #{object_type_text}",
                             :default_choice_text => "Select #{association.to_s.singularize.humanize} ...",
                             :name => "#{options[:object_class].name.underscore}[#{association.to_s.singularize}_ids]",
                             :possibilities => [],
                             :value_method => :id,
                             :text_method => :title,
                             :with_new_link => with_new_link,
                             :object_type_text=> object_type_text,
                             :association=>association

      options[:selected] = object.send(association).map(&options[:value_method]) unless options[:selected]

      super object, association, options
    end
  end

  module SetDefaultsWithReflection
    def fancy_multiselect object, association, options = {}

      if reflection = options[:object_class].reflect_on_association(association)
        required_access = reflection.options[:required_access] || :can_view?
        #get 'view' from :can_view?
        access = required_access.to_s.split('_').last.gsub('?','')
        association_class = options.delete(:association_class) || reflection.klass
        options[:project_possibilities] = authorised_assets(association_class, current_user.person.projects, access) if options[:other_projects_checkbox]
        options[:possibilities] = authorised_assets(association_class, nil, access) unless options[:possibilities]
      end

      super object, association, options
    end
  end

  module FoldingBox
    def fancy_multiselect object, association, options = {}
      hidden = options.delete(:hidden)
      object_type_text = options[:object_type_text] || options[:object_class].name.underscore.humanize
      object_type_text = (I18n.t 'biosamples.sample_parent_term') if object_type_text == 'Specimen'
      title = (help_icon("Here you can associate the #{object_type_text} with specific #{association.to_s.singularize.humanize.pluralize}.") + " #{association.to_s.singularize.humanize.pluralize}") + (options[:required] ? ' <span class="required">*</span>'.html_safe : '')

      folding_box "add_#{association}_form", title , :hidden => hidden, :contents => super(object, association, options)
    end
  end

  module AssociationContentFromBlock
    def fancy_multiselect object, association, options = {}
      if block_given?
        options[:association_step_content] = '' unless options[:association_step_content]
        options[:association_step_content] = options[:association_step_content] + capture {yield}
        concat super(object, association, options)
      else
        super(object, association, options)
      end
    end
  end


  module StringOrObject
    def fancy_multiselect string_or_object, association, options = {}
      string_or_object = string_or_object.constantize if string_or_object.is_a? String
      if string_or_object.is_a? Class
        options[:object_class] = string_or_object
        string_or_object = nil
      else
        options[:object_class] = string_or_object.class
      end
      super(string_or_object, association, options)
    end
  end

  #commenting some of these out should be ok. The only one relied on by others is SetDefaults (and Base, of course).
  #Changing the order may break things. These are executed starting with the last one, and ending with Base.
  include Base
  include AjaxPreview
  include HideButtonWhenDefaultIsSelected
  include OtherProjectsCheckbox
  include SetDefaults
  include SetDefaultsWithReflection
  include FoldingBox
  include AssociationContentFromBlock
  include StringOrObject
end