修复糟糕的Ruby代码

时间:2018-03-30 23:25:10

标签: ruby-on-rails arrays ruby

我有一个未定义的方法错误:

NoMethodError (undefined method `[]' for nil:NilClass):
  app/controllers/search_controller.rb:143:in `block in index'
  app/controllers/search_controller.rb:142:in `each_pair'
  app/controllers/search_controller.rb:142:in `index'

错误发生在以下3行:

results.each_pair do |k,v|
      @video_dates[v['videos'][0][:video_date]][k] = v
end

问题是,由于我没有在编码方面经验丰富,我无法修复它。以下是整个控制器的样子:

class SearchController < ApplicationController
  include ApplicationHelper
  include SearchHelper
  def index
    per_page = 50
    release = ActiveRecord::Base.connection.execute('select min(release),max(release) from serials where release<>0;').values[0]
    @release_min, @release_max = release[0].to_i,release[1].to_i
    allow_params = params.permit(:key,:search_elements,:non_search_elements, :tag_elements,:page,:page_a,:translator_id,:release_v1,:release_v2)
    @release_v1 = allow_params[:release_v1].to_i
    @release_v2 = allow_params[:release_v2].to_i
    @release_v1 = @release_min if @release_v1.zero?
    @release_v2 = @release_max if @release_v2.zero?
    @tag_elements = allow_params[:tag_elements]
    @translator_id = allow_params[:translator_id].to_i
    @key = allow_params[:key]
    @search_elements = allow_params[:search_elements]
    @non_search_elements = allow_params[:non_search_elements]
    search_elements = @search_elements ? @search_elements.split('-').map{|x| x.to_i}.select{|x| x>0} : []
    non_search_elements = @non_search_elements ? @non_search_elements.split('-').map{|x| x.to_i}.select{|x| x>0} : []
    tag_elements = @tag_elements ? @tag_elements.split('-').map{|x| x.to_i}.select{|x| x>0} : []
    additional_keys = [50001,50002,50003,50004,50005]
    search_elements_additional = search_elements & additional_keys
    non_search_elements_additional = non_search_elements & additional_keys
    search_elements = search_elements - search_elements_additional
    non_search_elements = non_search_elements - non_search_elements_additional
    cond=[]
    if (@release_min!=@release_v1) or (@release_max!=@release_v2)
      cond << "serial_id in (select id from serials where release between #{@release_v1} and #{@release_v2})"
    end
    unless @translator_id.zero?
      cond << "serial_id in (select distinct serial_id from videos where translator_id=#{@translator_id})"
    end
    unless @key.blank?
      @key = russian_lower(remove_sql_explot(@key)).downcase
      @key = @key[1,40] if @key.size>40
      if @key.size>=3
        cond << "serial_id in (select id from serials where lower(title) like '%#{@key}%' or
          lower(english_title) like '%#{@key}%' or lower(original_title) like '%#{@key}%') or
          serial_id in (select distinct serial_id from videos v
            join translators t on v.translator_id=t.id where lower(t.title) like '%#{@key}%') or
            serial_id in (select serial_id from serial_elements where element_id in
          (select id from elements  where elem_type in (3,4,5) and lower(title) like '%#{@key}%' ) group by serial_id)"
      end
    end
    if tag_elements.any?
      tags = Tag.where(id:tag_elements).map{|x|x.title}.sort[0,5]
      @tags = t('search.index.tags_confirm',tags:tags.join(',') + (tags.size==tag_elements.size ? '' : '...'))
      cond << "serial_id in (select serial_id from serial_tags where tag_id in (#{tag_elements.join(',')}))"
    end
    if search_elements_additional.any? or non_search_elements_additional.any?
      elements_cond = []
      total_cond = []
      total_cond << 'total in (12,13)' if search_elements_additional.include?(50003)
      total_cond << 'total in (24,25,26)' if search_elements_additional.include?(50004)
      total_cond << 'total >= 27' if search_elements_additional.include?(50005)
      elements_cond << "(#{total_cond.join(' or ')})" if total_cond.any?
      elements_cond << 'full_length = true' if search_elements_additional.include?(50001)
      elements_cond << 'adult = true' if search_elements_additional.include?(50002)
      elements_cond << 'full_length = false' if non_search_elements_additional.include?(50001)
      elements_cond << 'adult = false' if non_search_elements_additional.include?(50002)
      elements_cond << 'total not in (12,13)' if non_search_elements_additional.include?(50003)
      elements_cond << 'total not in (24,25,26)' if non_search_elements_additional.include?(50004)
      elements_cond << 'total < 27' if non_search_elements_additional.include?(50005)
      cond << "serial_id in (select id from serials where #{elements_cond.join(' and ')})" if elements_cond.any?
    end
    if search_elements.any? or non_search_elements.any?
      elements_cond = []
      elements_cond << "serial_id in (select serial_id from serial_elements
        where element_id in (#{search_elements.join(',')})
        group by serial_id having count(element_id)=#{search_elements.size})" if search_elements.any?
      elements_cond << "serial_id not in (select serial_id from serial_elements where element_id in (#{non_search_elements.join(',')}))" if non_search_elements.any?
      cond << "serial_id in (select serial_id from serial_elements where
        #{elements_cond.join(' and ')} group by serial_id)"
    end
    cond << 'chapter_id>=0'
    @page = allow_params[:page]
    @page_a = allow_params[:page_a]
    @genres = Element.genres
    @categories = Element.categories
    @additionals = [
        {id:50001,title:t('search.index.full_length')},
        {id:50002,title:t('search.index.adult')},
        {id:50003,title:t('search.index.total_1')},
        {id:50004,title:t('search.index.total_2')},
        {id:50005,title:t('search.index.total_3')}
    ]
    @page = [@page.to_i,1].max
    video = Video.select('serial_id, max(video_date) as video_date')
    video = video.where(cond.join(' and ')) if cond.any?
    @serial_ids = video.group(:serial_id).order('2 desc, serial_id').paginate(page:@page, per_page:per_page)
    ids = @serial_ids.map{|x| x.serial_id}
    serials = Serial.where(id:ids)
    results = {}
    ids.each{|id| results[id] = {}}
    serials.each do |serial|
      %w{title english_title original_title aka clear_link}.each do |attr|
        results[serial.id][attr] = serial.send(attr)
      end
    end
    images = SerialImage.select('id,serial_id').where(frame:false,serial_id:ids)
    images.each do |image|
      if results[image.serial_id].include?('images')
        results[image.serial_id]['images'] << image.id
      else
        results[image.serial_id]['images'] = [image.id]
      end
    end
    genres_and_categories = SerialElement.joins(:element).where('elements.elem_type'=>[1,2],'serial_id'=>ids).
        select('serial_id, title, elem_type, element_id')
    genres_and_categories.each do |elem|
      if results[elem.serial_id].include?('elements')
        results[elem.serial_id]['elements'] << {title:elem.title,type:elem.elem_type,id:elem.element_id}
      else
        results[elem.serial_id]['elements'] = [{title:elem.title,type:elem.elem_type,id:elem.element_id}]
      end
    end
    @serial_ids.each do |video|
      results[video.serial_id]['video_date'] = video.video_date
    end
    if ids.any?
      last_videos = ActiveRecord::Base.connection.
        execute("select v.serial_id,v.chapter_id,details,c.title,v.id,v.translator_id,t.title, video_date, c.episode_id,t.link from (
        select serial_id, chapter_id, details,id,translator_id, link, video_date, row_number()over (partition by serial_id
        order by video_date desc, chapter_id desc) as rownum from videos where chapter_id>=0 and serial_id IN
        (#{ids.join(',')})) v join chapters c on c.serial_id=v.serial_id and c.chapter_id=v.chapter_id
        left join translators t on t.id=v.translator_id where rownum < 2 order by serial_id,rownum").values
      last_videos =  leave_only_new_video last_videos
      last_videos.each do |row|
        k = row[0].to_i
        v = {id:row[4].to_i,details:row[2].strip,
             title:t('search.index.chapter_title',id:row[8].blank? ? row[1] : row[8]), translator_link:row[9],
             translator_id:row[5].to_i,translator_title:row[6].nil? ? '' :row[6],video_date:row[7].split(' ')[0],chapter_id:row[1].to_i}
        if results[row[0].to_i].include?('videos')
          results[k]['videos'] << v
        else
          results[k]['videos'] = [v]
        end
      end
      @video_dates = {}
      last_videos.map{|x|x[7].split(' ')[0]}.uniq.each{|x| @video_dates[x]={}}
    end
    results.each_pair do |k,v|
      @video_dates[v['videos'][0][:video_date]][k] = v
    end
    @video_keys = @video_dates.keys.sort.reverse if ids.any?
    @results = results
    unless @translator_id.zero?
      begin
        @translator = Translator.find(@translator_id)
      rescue
        redirect_to(root_path) and return
      end
    end
  end
end

我在Rails 4.0.0,Ruby 2.1.9上运行我的网站,这段代码有效,它会崩溃特定的请求,例如: 搜索由2个名称组成的标题,只有一个或甚至是一个或不同语言的标题(应该可以使用)。

1 个答案:

答案 0 :(得分:0)

该块中的

v['videos']仅设置在results的{​​{1}}成员中。我猜这一行:

last_videos

...正在从 last_videos = leave_only_new_video last_videos 删除一些results ID(可能是旧视频),这些都是导致错误的原因。

根据您实际想要完成的操作,您可能希望在进一步处理这些ID之前从last_videos中删除这些ID,或者为它们指定默认日期。