Class: DatastaxRails::Column

Inherits:
Object
  • Object
show all
Defined in:
lib/datastax_rails/column.rb

Overview

Contains the metadata for a given column. Also provides a number of helper methods to take cast values to the appropriate types (i.e., Ruby, CQL, Solr)

Defined Under Namespace

Modules: Format

Constant Summary collapse

TRUE_VALUES =

rubocop:disable Metrics/ClassLength

[true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON'].to_set
FALSE_VALUES =
[false, 0, '0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF'].to_set

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, default, type, options = {}) ⇒ Column

Instantiates a new column in the table.

name is the column’s name as specified in the schema. e.g., ‘first_name’ in first_name text. default is the type-casted default value that will be applied to a new record if no value is given. type is the type of the column. Usually this will match the cql_type, but there are exceptions (e.g., date) cql_type is the type of column as specified in the schema. e.g., ‘text’ in first_name text. solr_type overrides the normal CQL <-> SOLR type mapping (uncommon)



33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/datastax_rails/column.rb', line 33

def initialize(name, default, type, options = {})
  @name      = name
  @type      = type.to_sym
  fail ArgumentError, "Unknown type #{type}" unless klass
  options[:holds] = 'string' if collection? && options[:holds].blank?
  @options   = configure_options(@type, options).with_indifferent_access
  @cql_type  = compute_cql_type(@type, @options)
  @solr_type = compute_solr_type(@type, @options)
  @default   = extract_default(default)
  @primary   = nil
  @coder     = nil
end

Instance Attribute Details

#coderObject Also known as: encoded?

Returns the value of attribute coder.



18
19
20
# File 'lib/datastax_rails/column.rb', line 18

def coder
  @coder
end

#cql_typeObject (readonly)

Returns the value of attribute cql_type.



17
18
19
# File 'lib/datastax_rails/column.rb', line 17

def cql_type
  @cql_type
end

#defaultObject

Returns the value of attribute default.



18
19
20
# File 'lib/datastax_rails/column.rb', line 18

def default
  @default
end

#nameObject (readonly)

Returns the value of attribute name.



17
18
19
# File 'lib/datastax_rails/column.rb', line 17

def name
  @name
end

#optionsObject (readonly)

Returns the value of attribute options.



17
18
19
# File 'lib/datastax_rails/column.rb', line 17

def options
  @options
end

#primaryObject

Returns the value of attribute primary.



18
19
20
# File 'lib/datastax_rails/column.rb', line 18

def primary
  @primary
end

#solr_typeObject (readonly)

Returns the value of attribute solr_type.



17
18
19
# File 'lib/datastax_rails/column.rb', line 17

def solr_type
  @solr_type
end

#typeObject (readonly)

Returns the value of attribute type.



17
18
19
# File 'lib/datastax_rails/column.rb', line 17

def type
  @type
end

Class Method Details

.binary_to_string(value) ⇒ Object

Used to convert from BLOBs to Strings



245
246
247
248
# File 'lib/datastax_rails/column.rb', line 245

def binary_to_string(value)
  # TODO: Figure out what Cassandra's blobs look like
  value
end

.string_to_binary(value) ⇒ Object

Used to convert from Strings to BLOBs



239
240
241
242
# File 'lib/datastax_rails/column.rb', line 239

def string_to_binary(value)
  # TODO: Figure out what Cassandra's blobs look like
  value
end

.string_to_dummy_time(string) ⇒ Object



268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/datastax_rails/column.rb', line 268

def string_to_dummy_time(string)
  return string unless string.is_a?(String)
  return nil if string.empty?

  dummy_time_string = "2000-01-01 #{string}"

  fast_string_to_time(dummy_time_string) || begin
    time_hash = Date._parse(dummy_time_string)
    return nil if time_hash[:hour].nil?
    new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction))
  end
end

.string_to_time(string) ⇒ Object



261
262
263
264
265
266
# File 'lib/datastax_rails/column.rb', line 261

def string_to_time(string)
  return string unless string.is_a?(String)
  return nil if string.empty?

  fast_string_to_time(string) || fallback_string_to_time(string)
end

.value_to_boolean(value) ⇒ Object

convert something to a boolean



282
283
284
285
286
287
288
# File 'lib/datastax_rails/column.rb', line 282

def value_to_boolean(value)
  if value.is_a?(String) && value.empty?
    nil
  else
    TRUE_VALUES.include?(value)
  end
end

.value_to_date(value) ⇒ Object



250
251
252
253
254
255
256
257
258
259
# File 'lib/datastax_rails/column.rb', line 250

def value_to_date(value)
  if value.is_a?(String)
    return nil if value.empty?
    fast_string_to_date(value) || fallback_string_to_date(value)
  elsif value.respond_to?(:to_date)
    value.to_date
  else
    value
  end
end

.value_to_decimal(value) ⇒ Object

convert something to a BigDecimal



301
302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/datastax_rails/column.rb', line 301

def value_to_decimal(value)
  return nil if value.blank?
  # Using .class is faster than .is_a? and
  # subclasses of BigDecimal will be handled
  # in the else clause
  if value.class == BigDecimal
    value
  elsif value.respond_to?(:to_d)
    value.to_d
  else
    value.to_s.to_d
  end
end

.value_to_integer(value) ⇒ Object

Used to convert values to integer.



291
292
293
294
295
296
297
298
# File 'lib/datastax_rails/column.rb', line 291

def value_to_integer(value)
  case value
  when TrueClass, FalseClass
    value ? 1 : 0
  else
    value.blank? ? nil : value.to_i rescue nil
  end
end

.value_to_uuid(value) ⇒ Object

convert something to a TimeUuid



316
317
318
319
320
321
322
# File 'lib/datastax_rails/column.rb', line 316

def value_to_uuid(value)
  if value.is_a?(::Cql::Uuid)
    value
  else
    ::Cql::TimeUuid.new(value) rescue nil
  end
end

Instance Method Details

#binary?Boolean

Returns true if the column is of type binary

Returns:

  • (Boolean)


86
87
88
# File 'lib/datastax_rails/column.rb', line 86

def binary?
  [:binary].include?(type)
end

#collection?Boolean

Returns:

  • (Boolean)


90
91
92
# File 'lib/datastax_rails/column.rb', line 90

def collection?
  [:set, :list, :map].include?(type)
end

#configure_options(type, options) ⇒ Object



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
# File 'lib/datastax_rails/column.rb', line 46

def configure_options(type, options)
  case type.to_sym
  when :set, :list, :map then
    configure_options(options[:holds], options).merge(multi_valued: true)
  when :binary then
    { solr_index: false,   solr_store: false,
      multi_valued: false, sortable: false,
      tokenized: false,    fulltext: false,
      cql_index: false }
  when :boolean, :date, :time, :timestamp, :datetime, :float, :integer, :uuid, :long, :double then
    { solr_index: true,    solr_store: true,
      multi_valued: false, sortable: true,
      tokenized: false,    fulltext: false,
      cql_index: false }
  when :string then
    { solr_index: true,    solr_store: true,
      multi_valued: false, sortable: true,
      tokenized: false,    fulltext: true,
      cql_index: false }
  when :text then
    { solr_index: true,    solr_store: true,
      multi_valued: false, sortable: false,
      tokenized: true,     fulltext: true,
      cql_index: false }
  else
    fail ArgumentError, "Unknown Type: #{type}"
  end.merge(options)
end

#default?Boolean

Returns:

  • (Boolean)


94
95
96
# File 'lib/datastax_rails/column.rb', line 94

def default?
  default.present?
end

#empty_valueObject



98
99
100
# File 'lib/datastax_rails/column.rb', line 98

def empty_value
  extract_default(nil)
end

#extract_default(default) ⇒ Object



215
216
217
218
219
220
221
222
# File 'lib/datastax_rails/column.rb', line 215

def extract_default(default)
  default || case type
             when :map      then {}
             when :list     then []
             when :set      then Set.new
             else default
             end
end

#full_solr_rangeObject



229
230
231
232
233
234
235
# File 'lib/datastax_rails/column.rb', line 229

def full_solr_range
  if %w(date uuid integer int double long float).include? solr_type
    '[* TO *]'
  else
    '[\"\" TO *]'
  end
end

#human_nameObject

Returns the human name of the column name.

Examples
Column.new('sales_stage', ...).human_name # => 'Sales stage'


211
212
213
# File 'lib/datastax_rails/column.rb', line 211

def human_name
  Base.human_attribute_name(@name)
end

#klassObject

Returns the Ruby class that corresponds to the abstract data type.



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/datastax_rails/column.rb', line 103

def klass
  case type
  when :integer, :long                 then Fixnum
  when :float                          then Float
  when :double                         then BigDecimal
  when :timestamp, :time, :datetime    then Time
  when :date                           then Date
  when :text, :string, :binary, :ascii then String
  when :boolean                        then Object
  when :uuid                           then ::Cql::TimeUuid
  when :list                           then DatastaxRails::Types::DynamicList
  when :set                            then DatastaxRails::Types::DynamicSet
  when :map                            then DatastaxRails::Types::DynamicMap
  end
end

#list_to_cql3_value(value) ⇒ Object



198
199
200
# File 'lib/datastax_rails/column.rb', line 198

def list_to_cql3_value(value)
  value.map { |v| type_cast_for_cql3(v, @options[:holds].to_sym) }
end

#list_to_solr_value(value) ⇒ Object



190
191
192
# File 'lib/datastax_rails/column.rb', line 190

def list_to_solr_value(value)
  value.map { |v| type_cast_for_solr(v, @options[:holds].to_sym) }
end

#map_to_cql3_value(value) ⇒ Object



202
203
204
205
# File 'lib/datastax_rails/column.rb', line 202

def map_to_cql3_value(value)
  value.dup.each { |k, v| value[k] = type_cast_for_cql3(v, @options[:holds].to_sym) }
  value
end

#map_to_solr_value(value) ⇒ Object



194
195
196
# File 'lib/datastax_rails/column.rb', line 194

def map_to_solr_value(value)
  value.each { |k, v| value[k] = type_cast_for_solr(v, @options[:holds].to_sym) }
end

#number?Boolean

Returns true if the column is either of type integer, float or decimal.

Returns:

  • (Boolean)


81
82
83
# File 'lib/datastax_rails/column.rb', line 81

def number?
  [:double, :float, :integer, :long].include?(type)
end

#string_to_binary(value) ⇒ Object

Used to convert from Strings to BLOBs



225
226
227
# File 'lib/datastax_rails/column.rb', line 225

def string_to_binary(value)
  self.class.string_to_binary(value)
end

#text?Boolean

Returns true if the column is either of type ascii or text.

Returns:

  • (Boolean)


76
77
78
# File 'lib/datastax_rails/column.rb', line 76

def text?
  [:ascii, :text].include?(type)
end

#type_cast(value, record = nil, dest_type = nil) ⇒ Object

Casts value (which can be a String) to an appropriate instance.



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
# File 'lib/datastax_rails/column.rb', line 120

def type_cast(value, record = nil, dest_type = nil) # rubocop:disable Metrics/CyclomaticComplexity
  value = @default if value.nil?
  return nil if value.nil?
  return coder.load(value) if encoded?

  klass = self.class

  case dest_type || type
  when :string, :text        then value.to_s
  when :ascii                then value.force_encoding('ascii')
  when :integer, :long       then klass.value_to_integer(value)
  when :float                then value.to_f
  when :decimal              then klass.value_to_decimal(value)
  when :datetime, :timestamp then klass.string_to_time(value)
  when :time                 then klass.string_to_dummy_time(value)
  when :date                 then klass.value_to_date(value)
  when :binary               then klass.binary_to_string(value)
  when :boolean              then klass.value_to_boolean(value)
  when :uuid, :timeuuid
    uuid = klass.value_to_uuid(value)
    uuid.is_a?(::Cql::Uuid) ? uuid.to_s : uuid
  when :list, :set
    wrap_collection(Array(value).map { |v| type_cast(v, record, @options[:holds]) }.reject(&:blank?), record)
  when :map
    wrap_collection(value.each { |k, v| value[k] = type_cast(v, record, @options[:holds]) }.stringify_keys, record)
  else value
  end
end

#type_cast_for_cql3(value, dest_type = nil) ⇒ Object

Cql-rb does a really good job of typecasting, so for the most part we just pass in the native types. The only exceptions are for UUIDs that are passed in as strings and dates.



158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/datastax_rails/column.rb', line 158

def type_cast_for_cql3(value, dest_type = nil)
  return nil if value.nil?
  return coder.dump(value) if encoded?

  case (dest_type || type)
  when :uuid                        then value.is_a?(::Cql::Uuid) ? value : self.class.value_to_uuid(value)
  when :time, :datetime, :timestamp then value.to_time.utc
  when :date                        then value.to_time.utc
  when :set                         then Set.new(list_to_cql3_value(value))
  when :list                        then list_to_cql3_value(value)
  when :map                         then map_to_cql3_value(value)
  else value
  end
end

#type_cast_for_solr(value, dest_type = nil) ⇒ Object

By contrast, since Solr isn’t doing things like prepared statements it doesn’t know what the types are so we have to handle any casting or encoding ourselves.



176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/datastax_rails/column.rb', line 176

def type_cast_for_solr(value, dest_type = nil)
  return nil if value.nil?
  return coder.dump(value) if encoded?

  case (dest_type || type)
  when :boolean                            then value ? 'true' : 'false'
  when :date, :time, :datetime, :timestamp then value.to_time.utc.strftime(Format::SOLR_TIME_FORMAT)
  when :list, :set                         then list_to_solr_value(value)
  when :map                                then map_to_solr_value(value)
  when :uuid, :timeuuid                    then value.to_s
  else value
  end
end

#wrap_collection(collection, record) ⇒ Object



149
150
151
152
153
# File 'lib/datastax_rails/column.rb', line 149

def wrap_collection(collection, record)
  Types::DirtyCollection.ignore_modifications do
    klass.new(record, name, collection)
  end
end