Class: DatastaxRails::Column

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

Overview

rubocop:disable Style/ClassLength

Defined Under Namespace

Modules: Format

Constant Summary collapse

TRUE_VALUES =
[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)



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

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.



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

def coder
  @coder
end

#cql_typeObject (readonly)

Returns the value of attribute cql_type.



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

def cql_type
  @cql_type
end

#defaultObject

Returns the value of attribute default.



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

def default
  @default
end

#nameObject (readonly)

Returns the value of attribute name.



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

def name
  @name
end

#optionsObject (readonly)

Returns the value of attribute options.



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

def options
  @options
end

#primaryObject

Returns the value of attribute primary.



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

def primary
  @primary
end

#solr_typeObject (readonly)

Returns the value of attribute solr_type.



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

def solr_type
  @solr_type
end

#typeObject (readonly)

Returns the value of attribute type.



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

def type
  @type
end

Class Method Details

.binary_to_string(value) ⇒ Object

Used to convert from BLOBs to Strings



243
244
245
246
# File 'lib/datastax_rails/column.rb', line 243

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



237
238
239
240
# File 'lib/datastax_rails/column.rb', line 237

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

.string_to_dummy_time(string) ⇒ Object



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

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



259
260
261
262
263
264
# File 'lib/datastax_rails/column.rb', line 259

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



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

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



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

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



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

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.



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

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



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

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)


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

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

#collection?Boolean

Returns:

  • (Boolean)


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

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

#configure_options(type, options) ⇒ Object



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

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)


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

def default?
  default.present?
end

#empty_valueObject



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

def empty_value
  extract_default(nil)
end

#extract_default(default) ⇒ Object



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

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



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

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'


209
210
211
# File 'lib/datastax_rails/column.rb', line 209

def human_name
  Base.human_attribute_name(@name)
end

#klassObject

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



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

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



196
197
198
# File 'lib/datastax_rails/column.rb', line 196

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



188
189
190
# File 'lib/datastax_rails/column.rb', line 188

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



200
201
202
203
# File 'lib/datastax_rails/column.rb', line 200

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



192
193
194
# File 'lib/datastax_rails/column.rb', line 192

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)


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

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

#string_to_binary(value) ⇒ Object

Used to convert from Strings to BLOBs



223
224
225
# File 'lib/datastax_rails/column.rb', line 223

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)


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

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.



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

def type_cast(value, record = nil, dest_type = nil) # rubocop:disable Style/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.



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

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 :list, :set                  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.



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

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



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

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