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



234
235
236
237
# File 'lib/datastax_rails/column.rb', line 234

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



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

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

.string_to_dummy_time(string) ⇒ Object



257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/datastax_rails/column.rb', line 257

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



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

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



271
272
273
274
275
276
277
# File 'lib/datastax_rails/column.rb', line 271

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



239
240
241
242
243
244
245
246
247
248
# File 'lib/datastax_rails/column.rb', line 239

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



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

def value_to_decimal(value)
  # 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.



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

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

.value_to_uuid(value) ⇒ Object

convert something to a TimeUuid



304
305
306
307
308
309
310
# File 'lib/datastax_rails/column.rb', line 304

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 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

#extract_default(default) ⇒ Object



204
205
206
207
208
209
210
211
# File 'lib/datastax_rails/column.rb', line 204

def extract_default(default)
  case type
  when :map      then {} # lambda {|rec| DatastaxRails::Types::DynamicMap.new(rec, self.name.to_s, {})}
  when :list     then [] # lambda {|rec| DatastaxRails::Types::DynamicList.new(rec, self.name.to_s, [])}
  when :set      then Set.new # lambda {|set| DatastaxRails::Types::DynamicSet.new(rec, self.name.to_s, Set.new)}
  else default
  end
end

#full_solr_rangeObject



218
219
220
221
222
223
224
# File 'lib/datastax_rails/column.rb', line 218

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'


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

def human_name
  Base.human_attribute_name(@name)
end

#klassObject

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



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/datastax_rails/column.rb', line 98

def klass
  case type
  when :integer                        then Fixnum
  when :float                          then Float
  when :decimal, :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



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

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



179
180
181
# File 'lib/datastax_rails/column.rb', line 179

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



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

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



183
184
185
# File 'lib/datastax_rails/column.rb', line 183

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?
  [:decimal, :double, :float, :integer].include?(type)
end

#string_to_binary(value) ⇒ Object

Used to convert from Strings to BLOBs



214
215
216
# File 'lib/datastax_rails/column.rb', line 214

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.



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/datastax_rails/column.rb', line 115

def type_cast(value, record = nil, dest_type = nil) # rubocop:disable Style/CyclomaticComplexity
  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              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      then klass.value_to_uuid(value)
  when :list, :set           then wrap_collection(value.map { |v| type_cast(v, record, @options[:holds]) }, 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.



149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/datastax_rails/column.rb', line 149

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.



166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/datastax_rails/column.rb', line 166

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)
  else value
  end
end

#wrap_collection(collection, record) ⇒ Object



140
141
142
143
144
# File 'lib/datastax_rails/column.rb', line 140

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