超级合并来自父类的哈希

时间:2016-02-03 16:59:20

标签: ruby-on-rails ruby

我的API的V6中有以下私有方法:

def format_rentals(rentals)
  rentals.map do |rental|
    {
      start_time: rental.reservation_start_time.to_i,
       end_time: rental.reservation_end_time.to_i
     }
 end

在API的V10上。

V10继承自V9,V9来自V8,依旧......

我想要一个新方法的属性。

有没有办法不将整个方法剪切并粘贴到V10上,然后添加新属性。

我不想做的事情:

def format_rentals(rentals)
  rentals.map do |rental|
    {
      start_time: rental.reservation_start_time.to_i,
      end_time: rental.reservation_end_time.to_i,
      status: rental.status ### Adding this on V10

     }
 end

我很想打电话给super并合并到hash,到目前为止我已经尝试过了:

def format_rentals(rentals)
  rentals.map do |rental|
    super.merge(status: rental.status)
   end
end

我可能没有正确的语法...感谢您的帮助!

2 个答案:

答案 0 :(得分:2)

如果在超类中将块的内容提取到自己的format_rental方法中,这非常容易:

class V9 < V8
  def format_rental(rental)
    { start_time: rental.reservation_start_time.to_i,
      end_time: rental.reservation_end_time.to_i
    }
  end

  def format_rentals(rentals)
    rentals.map {|rental| format_rental(rental) }
  end
end

然后,在您的子类中,您只需覆盖format_rental

class V10 < V9
  def format_rental(rental)
    super.merge(status: rental.status)
    # Or: super.tap {|r| r[:status] = rental.status }
  end
end

super将返回带有:start_time:end_time键的哈希值;那么你只需要添加:status密钥即可。 format_rentals根本不需要修改。

答案 1 :(得分:1)

问题是您在传递给super的块内调用.map。这意味着在每次迭代中都会调用format_rentals。您可以编写一种格式化单个记录的方法:

def format_rentals(rentals)
  rentals.map { |r| format_rental(r) }
end


def format_rental(rental)
  super.merge(status: rental.status)
end

或者通过积木和收益获得创意。

然而,可能是退后一步并考虑这是否是一个好解决方案的好时机 - 您的控制器是否真的要负责序列化rentals?特别是如果您在不同版本的API中有不同的表示形式。我会考虑使用ActiveModel::Serializersjbuilder

class V9::RentalSerializer < ActiveModel::Serializer
  attributes :start_time, :end_time

  def start_time
    object.reservation_start_time.to_i
  end

  def end_time
    object.reservation_end_time.to_i
  end
end

class V10::RentalSerializer < V9::RentalSerializer
  attributes :start_time, :end_time, :status
end

- 根据API版本创建深层次结构也不是最佳选择。原因是它变成了一个非常精致的卡片屋,对于那些从第一天起就没有关注过这个项目的人来说,这是不可能破译的。

你可能想考虑拥有一个&#34; base&#34;它遵循当前版本和&#34;适配器&#34;旧版本的哪个backport。

class MyApi::RentalSerializerBase < ActiveModel::Serializer
  attributes :start_time, :end_time, :status

  def start_time
    object.reservation_start_time.to_i
  end

  def end_time
    object.reservation_end_time.to_i
  end
end

class V7::RentalSerializer < MyApi::RentalSerializerBase
  attributes :start_time, :end_time

  # lets say version 7 has a weird bug so that end_time is in iso 8601 instead
  # however since clients rely on the defect 
  # being there we cannot change the response
  def end_time
    object.reservation_end_time.iso8601
  end
end

class V10::RentalSerializer < MyApi::RentalSerializerBase; end