1# Protocol Buffers - Google's data interchange format 2# Copyright 2008 Google Inc. All rights reserved. 3# https://developers.google.com/protocol-buffers/ 4# 5# Redistribution and use in source and binary forms, with or without 6# modification, are permitted provided that the following conditions are 7# met: 8# 9# * Redistributions of source code must retain the above copyright 10# notice, this list of conditions and the following disclaimer. 11# * Redistributions in binary form must reproduce the above 12# copyright notice, this list of conditions and the following disclaimer 13# in the documentation and/or other materials provided with the 14# distribution. 15# * Neither the name of Google Inc. nor the names of its 16# contributors may be used to endorse or promote products derived from 17# this software without specific prior written permission. 18# 19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31require 'forwardable' 32 33# 34# This class makes RepeatedField act (almost-) like a Ruby Array. 35# It has convenience methods that extend the core C or Java based 36# methods. 37# 38# This is a best-effort to mirror Array behavior. Two comments: 39# 1) patches always welcome :) 40# 2) if performance is an issue, feel free to rewrite the method 41# in jruby and C. The source code has plenty of examples 42# 43# KNOWN ISSUES 44# - #[]= doesn't allow less used approaches such as `arr[1, 2] = 'fizz'` 45# - #concat should return the orig array 46# - #push should accept multiple arguments and push them all at the same time 47# 48module Google 49 module Protobuf 50 class RepeatedField 51 extend Forwardable 52 53 # methods defined in C or Java: 54 # + 55 # [], at 56 # []= 57 # concat 58 # clear 59 # dup, clone 60 # each 61 # push, << 62 # replace 63 # length, size 64 # == 65 # to_ary, to_a 66 # also all enumerable 67 # 68 # NOTE: using delegators rather than method_missing to make the 69 # relationship explicit instead of implicit 70 def_delegators :to_ary, 71 :&, :*, :-, :'<=>', 72 :assoc, :bsearch, :bsearch_index, :combination, :compact, :count, 73 :cycle, :dig, :drop, :drop_while, :eql?, :fetch, :find_index, :flatten, 74 :include?, :index, :inspect, :join, 75 :pack, :permutation, :product, :pretty_print, :pretty_print_cycle, 76 :rassoc, :repeated_combination, :repeated_permutation, :reverse, 77 :rindex, :rotate, :sample, :shuffle, :shelljoin, :slice, 78 :to_s, :transpose, :uniq, :| 79 80 81 def first(n=nil) 82 n ? self[0..n] : self[0] 83 end 84 85 86 def last(n=nil) 87 n ? self[(self.size-n-1)..-1] : self[-1] 88 end 89 90 91 def pop(n=nil) 92 if n 93 results = [] 94 n.times{ results << pop_one } 95 return results 96 else 97 return pop_one 98 end 99 end 100 101 102 def empty? 103 self.size == 0 104 end 105 106 # array aliases into enumerable 107 alias_method :each_index, :each_with_index 108 alias_method :slice, :[] 109 alias_method :values_at, :select 110 alias_method :map, :collect 111 112 113 class << self 114 def define_array_wrapper_method(method_name) 115 define_method(method_name) do |*args, &block| 116 arr = self.to_a 117 result = arr.send(method_name, *args) 118 self.replace(arr) 119 return result if result 120 return block ? block.call : result 121 end 122 end 123 private :define_array_wrapper_method 124 125 126 def define_array_wrapper_with_result_method(method_name) 127 define_method(method_name) do |*args, &block| 128 # result can be an Enumerator, Array, or nil 129 # Enumerator can sometimes be returned if a block is an optional argument and it is not passed in 130 # nil usually specifies that no change was made 131 result = self.to_a.send(method_name, *args, &block) 132 if result 133 new_arr = result.to_a 134 self.replace(new_arr) 135 if result.is_a?(Enumerator) 136 # generate a fresh enum; rewinding the exiting one, in Ruby 2.2, will 137 # reset the enum with the same length, but all the #next calls will 138 # return nil 139 result = new_arr.to_enum 140 # generate a wrapper enum so any changes which occur by a chained 141 # enum can be captured 142 ie = ProxyingEnumerator.new(self, result) 143 result = ie.to_enum 144 end 145 end 146 result 147 end 148 end 149 private :define_array_wrapper_with_result_method 150 end 151 152 153 %w(delete delete_at delete_if shift slice! unshift).each do |method_name| 154 define_array_wrapper_method(method_name) 155 end 156 157 158 %w(collect! compact! fill flatten! insert reverse! 159 rotate! select! shuffle! sort! sort_by! uniq!).each do |method_name| 160 define_array_wrapper_with_result_method(method_name) 161 end 162 alias_method :keep_if, :select! 163 alias_method :map!, :collect! 164 alias_method :reject!, :delete_if 165 166 167 # propagates changes made by user of enumerator back to the original repeated field. 168 # This only applies in cases where the calling function which created the enumerator, 169 # such as #sort!, modifies itself rather than a new array, such as #sort 170 class ProxyingEnumerator < Struct.new(:repeated_field, :external_enumerator) 171 def each(*args, &block) 172 results = [] 173 external_enumerator.each_with_index do |val, i| 174 result = yield(val) 175 results << result 176 #nil means no change occured from yield; usually occurs when #to_a is called 177 if result 178 repeated_field[i] = result if result != val 179 end 180 end 181 results 182 end 183 end 184 185 186 end 187 end 188end 189