Module: Google::Cloud::Spanner::Convert::ClassMethods

Included in:
Google::Cloud::Spanner::Convert
Defined in:
lib/google/cloud/spanner/convert.rb

Overview

rubocop:disable all

Instance Method Summary collapse

Instance Method Details

#duration_to_number(duration) ⇒ Object



220
221
222
223
224
225
226
# File 'lib/google/cloud/spanner/convert.rb', line 220

def duration_to_number duration
  return nil if duration.nil?

  return duration.seconds if duration.nanos == 0

  duration.seconds + (duration.nanos / 1000000000.0)
end

#number_to_duration(number) ⇒ Object



212
213
214
215
216
217
218
# File 'lib/google/cloud/spanner/convert.rb', line 212

def number_to_duration number
  return nil if number.nil?

  Google::Protobuf::Duration.new \
    seconds: number.to_i,
    nanos: (number.remainder(1) * 1000000000).round
end

#raw_to_value(obj) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/google/cloud/spanner/convert.rb', line 117

def raw_to_value obj
  if NilClass === obj
    Google::Protobuf::Value.new null_value: :NULL_VALUE
  elsif String === obj
    Google::Protobuf::Value.new string_value: obj
  elsif Symbol === obj
    Google::Protobuf::Value.new string_value: obj.to_s
  elsif TrueClass === obj
    Google::Protobuf::Value.new bool_value: true
  elsif FalseClass === obj
    Google::Protobuf::Value.new bool_value: false
  elsif Integer === obj
    Google::Protobuf::Value.new string_value: obj.to_s
  elsif Numeric === obj # Any number not an integer gets to be a float
    if obj == Float::INFINITY
      Google::Protobuf::Value.new string_value: "Infinity"
    elsif obj == -Float::INFINITY
      Google::Protobuf::Value.new string_value: "-Infinity"
    elsif obj.respond_to?(:nan?) && obj.nan?
      Google::Protobuf::Value.new string_value: "NaN"
    else
      Google::Protobuf::Value.new number_value: obj.to_f
    end
  elsif Time === obj || DateTime === obj
    Google::Protobuf::Value.new(string_value:
      obj.to_time.utc.strftime("%FT%T.%NZ"))
  elsif Date === obj
    Google::Protobuf::Value.new string_value: obj.to_s
  elsif Array === obj
    Google::Protobuf::Value.new list_value:
      Google::Protobuf::ListValue.new(values:
        obj.map { |o| raw_to_value(o) })
  elsif Hash === obj
    Google::Protobuf::Value.new struct_value:
      Google::Protobuf::Struct.new(fields:
        Hash[obj.map { |k, v| [String(k), raw_to_value(v)] }])
  elsif obj.respond_to?(:read) && obj.respond_to?(:rewind)
    obj.rewind
    content = obj.read.force_encoding("ASCII-8BIT")
    encoded_content = Base64.strict_encode64(content)
    Google::Protobuf::Value.new(string_value: encoded_content)
  else
    raise ArgumentError,
          "A value of type #{obj.class} is not supported."
  end
end

#raw_to_value_and_type(obj, type = nil) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/google/cloud/spanner/convert.rb', line 39

def raw_to_value_and_type obj, type = nil
  if NilClass === obj
    if type
      if type.is_a?(Array) && type.count == 1
        [Google::Protobuf::Value.new(null_value: :NULL_VALUE),
         Google::Spanner::V1::Type.new(
           code: :ARRAY, array_element_type:
            Google::Spanner::V1::Type.new(code: type.first))]
      # elsif type.is_a? Fields
      else
        [Google::Protobuf::Value.new(null_value: :NULL_VALUE),
         Google::Spanner::V1::Type.new(code: type)]
      end
    else
      raise ArgumentError, "Must provide type for nil values."
    end
  elsif String === obj
    [raw_to_value(obj), Google::Spanner::V1::Type.new(code: :STRING)]
  elsif Symbol === obj
    [raw_to_value(obj.to_s),
     Google::Spanner::V1::Type.new(code: :STRING)]
  elsif TrueClass === obj
    [raw_to_value(obj), Google::Spanner::V1::Type.new(code: :BOOL)]
  elsif FalseClass === obj
    [raw_to_value(obj), Google::Spanner::V1::Type.new(code: :BOOL)]
  elsif Integer === obj
    [raw_to_value(obj.to_s),
     Google::Spanner::V1::Type.new(code: :INT64)]
  elsif Numeric === obj # Any number not an integer gets to be a float
    [raw_to_value(obj),
     Google::Spanner::V1::Type.new(code: :FLOAT64)]
  elsif Time === obj || DateTime === obj
    [raw_to_value(obj),
     Google::Spanner::V1::Type.new(code: :TIMESTAMP)]
  elsif Date === obj
    [raw_to_value(obj.to_s),
     Google::Spanner::V1::Type.new(code: :DATE)]
  elsif Array === obj
    if type && !type.is_a?(Array)
      raise ArgumentError, "Array values must have an Array type."
    end
    type ||= begin
      # Find the param type for the first non-nil item the list
      nested_param_value = obj.detect { |x| !x.nil? }
      if nested_param_value.nil?
        raise ArgumentError, "Array values must have an Array type."
      end
      [raw_to_value_and_type(nested_param_value).last.code]
    end
    [raw_to_value(obj),
     Google::Spanner::V1::Type.new(
      code: :ARRAY, array_element_type:
        Google::Spanner::V1::Type.new(code: type.first))]
  elsif Hash === obj
    field_pairs = obj.map do |key, value|
      [key, raw_to_value_and_type(value).last]
    end
    formatted_fields = field_pairs.map do |name, param_type|
      Google::Spanner::V1::StructType::Field.new(
        name: String(name), type: param_type
      )
    end
    [raw_to_value(obj),
     Google::Spanner::V1::Type.new(
      code: :STRUCT,
      struct_type: Google::Spanner::V1::StructType.new(
        fields: formatted_fields
      ))]
  elsif obj.respond_to?(:read) && obj.respond_to?(:rewind)
    obj.rewind
    [raw_to_value(obj),
     Google::Spanner::V1::Type.new(code: :BYTES)]
  else
    raise ArgumentError,
          "A parameter of type #{obj.class} is not supported."
  end
end

#row_to_pairs(row_types, row) ⇒ Object



164
165
166
167
168
# File 'lib/google/cloud/spanner/convert.rb', line 164

def row_to_pairs row_types, row
  row_types.zip(row).map do |field, value|
    [field.name.to_sym, value_to_raw(value, field.type)]
  end
end

#row_to_raw(row_types, row) ⇒ Object



170
171
172
# File 'lib/google/cloud/spanner/convert.rb', line 170

def row_to_raw row_types, row
  Hash[row_to_pairs(row_types, row)]
end

#time_to_timestamp(time) ⇒ Object



228
229
230
231
232
233
234
235
236
237
# File 'lib/google/cloud/spanner/convert.rb', line 228

def time_to_timestamp time
  return nil if time.nil?

  # Force the object to be a Time object.
  time = time.to_time

  Google::Protobuf::Timestamp.new \
    seconds: time.to_i,
    nanos: time.nsec
end

#timestamp_to_time(timestamp) ⇒ Object



239
240
241
242
243
# File 'lib/google/cloud/spanner/convert.rb', line 239

def timestamp_to_time timestamp
  return nil if timestamp.nil?

  Time.at timestamp.seconds, Rational(timestamp.nanos, 1000)
end

#to_key_range(range) ⇒ Object



245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/google/cloud/spanner/convert.rb', line 245

def to_key_range range
  range_opts = {
    start_closed: raw_to_value(Array(range.begin)).list_value,
    end_closed: raw_to_value(Array(range.end)).list_value }

  if range.respond_to?(:exclude_begin?) && range.exclude_begin?
    range_opts[:start_open] = range_opts[:start_closed]
    range_opts.delete :start_closed
  end
  if range.exclude_end?
    range_opts[:end_open] = range_opts[:end_closed]
    range_opts.delete :end_closed
  end

  Google::Spanner::V1::KeyRange.new range_opts
end

#to_query_params(params, types = nil) ⇒ Object



31
32
33
34
35
36
37
# File 'lib/google/cloud/spanner/convert.rb', line 31

def to_query_params params, types = nil
  types ||= {}
  formatted_params = params.map do |key, obj|
    [String(key), raw_to_value_and_type(obj, types[key])]
  end
  Hash[formatted_params]
end

#value_to_raw(value, type) ⇒ Object



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/google/cloud/spanner/convert.rb', line 174

def value_to_raw value, type
  return nil if value.kind == :null_value
  case type.code
  when :BOOL
    value.bool_value
  when :INT64
    Integer value.string_value
  when :FLOAT64
    if value.kind == :string_value
      if value.string_value == "Infinity"
        Float::INFINITY
      elsif value.string_value == "-Infinity"
        -Float::INFINITY
      elsif value.string_value == "NaN"
        Float::NAN
      else
        Float value.string_value
      end
    else
      value.number_value
    end
  when :TIMESTAMP
    Time.parse value.string_value
  when :DATE
    Date.parse value.string_value
  when :STRING
    value.string_value
  when :BYTES
    StringIO.new Base64.decode64 value.string_value
  when :ARRAY
    value.list_value.values.map do |v|
      value_to_raw v, type.array_element_type
    end
  when :STRUCT
    Data.from_grpc value.list_value.values, type.struct_type.fields
  end
end