Search code examples
jqueryruby-on-railsdatatableserver-side

undefined method `map' for nil:NilClass with DataTables


I have just implemented datatables based off Ryan Bates' rails cast #340 'http://railscasts.com/episodes/340-datatables?autoplay=true' and I am receiving this error 'undefined method `map' for nil:NilClass'. Everywhere I have looked people are saying it is because what you are trying to call is undefined but in this case I am unsure why this is an error as this is being defined and I have followed this rails cast to the last detail.

The error i'm getting is "undefined method `map' for nil:NilClass" The stack trace is as follows:

Showing /Users/calligouser/Documents/CalligoProjects/cloudcentrev2/app/views/ips/index.html.erb where line #7 raised:

<li class="left"><%= select_tag 'ip_status_id', options_for_select(@ip_statuses, @status_id), { prompt: "All" } %></li>

This error .map is being made in the controller when defining @ip_statuses. The code goes to the model where it retrieves a hash of values as follows:

STATUSES = { 0 => "Unallocated",
           1 => "Allocated",
           2 => "Reserved",
           3 => "Transient"
}

I am trying to load a datatable of IP addresses in an IP index view.

IP Controller (index)

def index

    @ips = Ip.all

    respond_to do |format|
      format.html
      format.json { render json: IpsDatatable.new(view_context) }
    end

    @ip_statuses = Ip::STATUSES.map {|key,value| [value,key]}
end

ips_datatable.rb file for datatables options

class IpsDatatable
  delegate :params, :h, :link_to, :number_to_currency, to: :@view

  def initialise(view)
    @view = view
  end

  def as_json(options = {})
    {
      sEcho: params[:sEcho].to_i
      iTotalRecords: Ip.count,
      iTotalDisplayRecords: ips.total_entries,
      aaData: data
    }
  end


private

  def data
    ips.map do |ip|
      [
        link_to(ip.ip_address, ip),
        h(ip.system_name),
        h(ip.description),
        h(ip.system_location),
        h(ip.status)
      ]
    end
  end

  def ips
    @ips ||= fetch_ips
  end

  def fetch_ips
    ips = Ip.order("#{sort_column} #{sort_direction}")
    ips = ips.page(page).per_page(per_page)
    if params[:sSearch].present?
      ips = ips.where("name like :search or category like :search", search: "%#{params[:sSearch]}")
    end
    ips
  end

  def page
    params[:iDisplayStart].to_i/per_page + 1
  end

  def per_page
    params[:iDisplayLength].to_i > 0 ? params[:iDisplayLength].to_i : 10
  end

  def sort_column
    columns = %w[ip_address system_name system_location description status]
    columns[params[:iSortCol_0].to_i]
  end

  def sort_direction
    params[:sSortDir0] == "desc" ? "desc" : "asc"
  end

end

IPs JQuery file

 jQuery ->

  # -------------------------------------------------------------------------
  # DataTables Server Side
  # -------------------------------------------------------------------------
  $('#ip-table').dataTable
    sPaginationType: "full_numbers"
    bJQueryUI: true
    bProcessing: true
    bServerSide: true
    sAjaxSource: $('#ip-table').data('source')

IP Table form view

<table id="ip-table" class="full display" data-source="<%= products_url(format: "json") %>">
        <thead>
          <tr>
            <th>Ip address</th>
            <th>System name</th>
            <th>Description</th>
            <th>System location</th>
            <th>Status</th>
            <th>Edit / Delete</th>
          </tr>
        </thead>

        <tbody>

        </tbody>

      </table>

EDIT: I have found that the application doesn't reach beyond the respond_to block, so I attempted to debug within the ip_datatables file and the application seems to be failing before reaching the code in the file. I used a puts '....' after format.json line of code and it does reach this, but for some reason it doesn't return from within the ip_datatables file.


Solution

  • OK I actually found the problem to this. In the controller I removed everything other than the respond to block and placed this code above it:

    @ip_statuses = Ip::STATUSES.map {|key,value| [value,key]}
    
    if !session[:ip_status_id].nil? then @status_id = session[:ip_status_id] else @status_id = 1 end
    @ips = @ips.where(status: @status_id) unless @status_id == ""
    

    I then removed the :h from the delegation in the ips_datatable file and removed all instances of the 'h' method being used. I then filled in missing commas in the ips_datatable file.

    I also had to remove the edit / delete header from the html table as the datatable won't recognise this as it isn't actually a column in the database.