class DatastaxRails::Column

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)

Constants

FALSE_VALUES
TRUE_VALUES

Attributes

coder[RW]
cql_type[R]
default[RW]
encoded?[RW]
name[R]
options[R]
primary[RW]
solr_type[R]
type[R]

Public Class Methods

binary_to_string(value) click to toggle source

Used to convert from BLOBs to Strings

# File lib/datastax_rails/column.rb, line 245
def binary_to_string(value)
  # TODO: Figure out what Cassandra's blobs look like
  value
end
new(name, default, type, options = {}) click to toggle source

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)

# 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
string_to_binary(value) click to toggle source

Used to convert from Strings to BLOBs

# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source

convert something to a boolean

# 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) click to toggle source
# 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) click to toggle source

convert something to a BigDecimal

# 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) click to toggle source

Used to convert values to integer.

# 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) click to toggle source

convert something to a TimeUuid

# 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

Protected Class Methods

fallback_string_to_date(string) click to toggle source
# File lib/datastax_rails/column.rb, line 371
def fallback_string_to_date(string)
  new_date(*::Date._parse(string, false).values_at(:year, :mon, :mday))
end
fallback_string_to_time(string) click to toggle source
# File lib/datastax_rails/column.rb, line 375
def fallback_string_to_time(string)
  time_hash = Date._parse(string)
  time_hash[:sec_fraction] = microseconds(time_hash)

  new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction))
end
fast_string_to_date(string) click to toggle source
# File lib/datastax_rails/column.rb, line 351
def fast_string_to_date(string)
  if string =~ Format::ISO_DATE
    new_date Regexp.last_match[1].to_i, Regexp.last_match[2].to_i, Regexp.last_match[3].to_i
  end
end
fast_string_to_time(string) click to toggle source

Doesn't handle time zones.

# File lib/datastax_rails/column.rb, line 358
def fast_string_to_time(string)
  return unless string =~ Format::ISO_DATETIME
  microsec = (Regexp.last_match[7].to_r * 1_000_000).to_i
  new_time(Regexp.last_match[1].to_i,
           Regexp.last_match[2].to_i,
           Regexp.last_match[3].to_i,
           Regexp.last_match[4].to_i,
           Regexp.last_match[5].to_i,
           Regexp.last_match[6].to_i,
           microsec
          )
end
microseconds(time) click to toggle source

'0.123456' -> 123456 '1.123456' -> 123456

# File lib/datastax_rails/column.rb, line 328
def microseconds(time)
  time[:sec_fraction] ? (time[:sec_fraction] * 1_000_000).to_i : 0
end
new_date(year, mon, mday) click to toggle source
# File lib/datastax_rails/column.rb, line 332
def new_date(year, mon, mday)
  year && year != 0 && Date.new(year, mon, mday) rescue nil
end
new_time(year, mon, mday, hour, min, sec, microsec, offset = nil) click to toggle source
# File lib/datastax_rails/column.rb, line 336
def new_time(year, mon, mday, hour, min, sec, microsec, offset = nil) # rubocop:disable Metrics/ParameterLists
  # Treat 0000-00-00 00:00:00 as nil.
  return nil if year.nil? || (year == 0 && mon == 0 && mday == 0)

  if offset
    time = Time.utc(year, mon, mday, hour, min, sec, microsec) rescue nil
    return nil unless time

    time -= offset
    Base.default_timezone == :utc ? time : time.getlocal
  else
    Time.public_send(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
  end
end

Public Instance Methods

binary?() click to toggle source

Returns true if the column is of type binary

# File lib/datastax_rails/column.rb, line 86
def binary?
  [:binary].include?(type)
end
collection?() click to toggle source
# File lib/datastax_rails/column.rb, line 90
def collection?
  [:set, :list, :map].include?(type)
end
configure_options(type, options) click to toggle source
# 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?() click to toggle source
# File lib/datastax_rails/column.rb, line 94
def default?
  default.present?
end
empty_value() click to toggle source
# File lib/datastax_rails/column.rb, line 98
def empty_value
  extract_default(nil)
end
extract_default(default) click to toggle source
# 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_range() click to toggle source
# 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_name() click to toggle source

Returns the human name of the column name.

Examples
Column.new('sales_stage', ...).human_name # => 'Sales stage'
# File lib/datastax_rails/column.rb, line 211
def human_name
  Base.human_attribute_name(@name)
end
klass() click to toggle source

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

# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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?() click to toggle source

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

# File lib/datastax_rails/column.rb, line 81
def number?
  [:double, :float, :integer, :long].include?(type)
end
string_to_binary(value) click to toggle source

Used to convert from Strings to BLOBs

# File lib/datastax_rails/column.rb, line 225
def string_to_binary(value)
  self.class.string_to_binary(value)
end
text?() click to toggle source

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

# File lib/datastax_rails/column.rb, line 76
def text?
  [:ascii, :text].include?(type)
end
type_cast(value, record = nil, dest_type = nil) click to toggle source

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

# 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) click to toggle source

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.

# 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) click to toggle source

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.

# 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) click to toggle source
# 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

Private Instance Methods

compute_cql_type(field_type, options) click to toggle source
# File lib/datastax_rails/column.rb, line 385
def compute_cql_type(field_type, options)
  options[:cql_type] ||
    case field_type.to_sym
    when :integer                            then 'int'
    when :long                               then 'bigint'
    when :time, :date, :timestamp, :datetime then 'timestamp'
    when :binary                             then 'blob'
    when :list                               then "list<#{compute_cql_type(options[:holds], options)}>"
    when :set                                then "set<#{compute_cql_type(options[:holds], options)}>"
    when :map                                then "map<text, #{compute_cql_type(options[:holds], options)}>"
    when :string                             then 'text'
    else field_type.to_s
    end
end
compute_solr_type(field_type, options) click to toggle source
# File lib/datastax_rails/column.rb, line 400
def compute_solr_type(field_type, options)
  options[:solr_type] ||
    case field_type.to_sym
    when :integer                            then 'int'
    when :timestamp, :time, :datetime        then 'date'
    when :list, :set, :map                   then compute_solr_type(options[:holds], options)
    else field_type.to_s
    end
end