Class: BigRecord::ConnectionAdapters::Column

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

Overview

Column/Attribute Definition

As long as a model has at least one column family set up for it, then columns (a.k.a. model attributes) can then be defined for the model.

The following is an example of a model named book.rb that has a column family called "attribute" set up for it:

  class Book < BigRecord::Base
    column 'attribute:title',   :string
    column :author,             :string
    column :description,        :string
    column :links,              :string,  :collection => true
  end

This simple model defines 4 columns of type string. An important thing to notice here is that the first column ‘attribute:title’ has the column family prepended to it. This is identical to just passing the symbol :title to the column method, and the default behaviour is to prepend the column family (attribute) automatically if one is not defined.

Furthermore, in HBase, there’s the option of storing collections for a given column. This will return an array for the links attribute on a Book record.

Types and Options

See Also:

Defined Under Namespace

Modules: Format

Constant Summary

COLLECTION_SEPARATOR =
"::"

Instance Attribute Summary

Class Method Summary

Instance Method Summary

Constructor Details

- (Column) initialize(name, type, options = {})

A new instance of Column



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/big_record/connection_adapters/column.rb', line 48

def initialize(name, type, options={})
  @type       = type.to_sym
  @collection = options[:collection]
  @name       = name.to_s
  @alias      = options[:alias] ? options[:alias].to_s : (self.class.extract_qualifier(@name) || (@name unless family?))

  if options[:default]
    @default = options[:default]
  elsif @collection
    @default = []
  else
    @default = (@type == :boolean) ? false : nil
  end
  # cache whether or not we'll need to dup the default columns to avoid clients to share
  # the same reference to the default value
  @must_dup_default = (!@default.nil? and (collection or (!number? and @type != :boolean)))

  @primary          = nil
end

Instance Attribute Details

- (Object) alias (readonly)

Returns the value of attribute alias



43
44
45
# File 'lib/big_record/connection_adapters/column.rb', line 43

def alias
  @alias
end

- (Object) collection (readonly)

Returns the value of attribute collection



43
44
45
# File 'lib/big_record/connection_adapters/column.rb', line 43

def collection
  @collection
end

- (Object) default (readonly)

Returns the value of attribute default



43
44
45
# File 'lib/big_record/connection_adapters/column.rb', line 43

def default
  @must_dup_default ? @default.dup : @default
end

- (Object) name (readonly)

Returns the value of attribute name



43
44
45
# File 'lib/big_record/connection_adapters/column.rb', line 43

def name
  @name
end

- (Object) primary

Returns the value of attribute primary



44
45
46
# File 'lib/big_record/connection_adapters/column.rb', line 44

def primary
  @primary
end

- (Object) type (readonly)

Returns the value of attribute type



43
44
45
# File 'lib/big_record/connection_adapters/column.rb', line 43

def type
  @type
end

Class Method Details

+ (Object) binary_to_string(value)

Used to convert from BLOBs to Strings



362
363
364
# File 'lib/big_record/connection_adapters/column.rb', line 362

def binary_to_string(value)
  value
end

+ (Boolean) blank_or_invalid_record?(record_attributes)

Check if the given record is empty. It’s recursive since it can be an Embedded

Returns:

  • (Boolean)


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

def blank_or_invalid_record?(record_attributes)
  return true if record_attributes.blank? or !record_attributes.is_a?(Hash)
  record_attributes.all? do |k, v|
    v.is_a?(Hash) ? (v.empty? or blank_or_invalid_record?(v)) : v.blank?
  end
end

+ (Object) extract_callstack_for_multiparameter_attributes(pairs)



287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/big_record/connection_adapters/column.rb', line 287

def extract_callstack_for_multiparameter_attributes(pairs)
  attributes = { }

  for pair in pairs
    multiparameter_name, value = pair
    attribute_name = multiparameter_name.split("(").first
    attributes[attribute_name] = [] unless attributes.include?(attribute_name)

    unless value.empty?
      attributes[attribute_name] <<
        [ find_parameter_position(multiparameter_name), type_cast_attribute_value(multiparameter_name, value) ]
    end
  end

  attributes.each { |name, values| attributes[name] = values.sort_by{ |v| v.first }.collect { |v| v.last } }
end

+ (Object) extract_family(column_name)

Extract the family from a column name



239
240
241
242
243
# File 'lib/big_record/connection_adapters/column.rb', line 239

def extract_family(column_name)
  return nil unless column_name
  column_name =~ /\A(.*?:).*\Z/
  $1
end

+ (Object) extract_qualifier(column_name)

Extract the qualifier from a column name



246
247
248
249
250
# File 'lib/big_record/connection_adapters/column.rb', line 246

def extract_qualifier(column_name)
  return nil unless column_name
  column_name =~ /\A.*?:(.*)\Z/
  $1
end

+ (Object) hash_to_boolean_collection(value)



352
353
354
# File 'lib/big_record/connection_adapters/column.rb', line 352

def hash_to_boolean_collection(value)
  parse_collection(value).collect{|v| value_to_boolean(v)}
end

+ (Object) hash_to_collection(hash)

Extract the collection from the hash, where the positions are the keys. Inspired from ActiveRecord::NestedAttributes.

  params = { 'member' => {
    'name' => 'joe', 'posts_attributes' => {
      '1' => { 'title' => 'Kari, the awesome Ruby documentation browser!' },
      '2' => { 'title' => 'The egalitarian assumption of the modern citizen' },
      'new_67890' => { 'title' => '' } # This one matches the :reject_if proc and will not be instantiated.
    }
  }}


262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/big_record/connection_adapters/column.rb', line 262

def hash_to_collection(hash)
  return hash unless hash.is_a?(Hash)

  # Make sure any new records sorted by their id before they're build.
  sorted_by_id = hash.sort_by { |id, _| id.is_a?(String) ? id.sub(/^new_/, '').to_i : id }

  array = []
  sorted_by_id.each do |id, record_attributes|
    # remove blank records
    next if blank_or_invalid_record?(record_attributes)

    array << record_attributes
  end
  array
end

+ (Object) hash_to_date_collection(value)



340
341
342
# File 'lib/big_record/connection_adapters/column.rb', line 340

def hash_to_date_collection(value)
  parse_collection(value).collect{|v| string_to_date(v.to_s)}
end

+ (Object) hash_to_decimal_collection(value)



336
337
338
# File 'lib/big_record/connection_adapters/column.rb', line 336

def hash_to_decimal_collection(value)
  parse_collection(value).collect{|v| value_to_decimal(v)}
end

+ (Object) hash_to_dummy_time_collection(value)



348
349
350
# File 'lib/big_record/connection_adapters/column.rb', line 348

def hash_to_dummy_time_collection(value)
  parse_collection(value).collect{|v| string_to_dummy_time(v.to_s)}
end

+ (Object) hash_to_float_collection(value)



332
333
334
# File 'lib/big_record/connection_adapters/column.rb', line 332

def hash_to_float_collection(value)
  parse_collection(value).collect(&:to_f)
end

+ (Object) hash_to_integer_collection(value)



319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/big_record/connection_adapters/column.rb', line 319

def hash_to_integer_collection(value)
  ret = nil
  begin
    collection = parse_collection(value)
    ret = collection.collect(&:to_i)
  rescue
    $stderr.puts("expected an array, got #{collection}, (#{collection.class})\n"+
      "original value fed into parse_collection was #{value}, (#{value.class})")
    ret = [collection.to_i]
  end
  ret
end

+ (Object) hash_to_string_collection(value)

strings are a special case…



315
316
317
# File 'lib/big_record/connection_adapters/column.rb', line 315

def hash_to_string_collection(value)
  parse_collection(value).collect(&:to_s)
end

+ (Object) hash_to_time_collection(value)



344
345
346
# File 'lib/big_record/connection_adapters/column.rb', line 344

def hash_to_time_collection(value)
  parse_collection(value).collect{|v| string_to_time(v.to_s)}
end

+ (Object) parse_collection(value)



304
305
306
307
308
309
310
311
312
# File 'lib/big_record/connection_adapters/column.rb', line 304

def parse_collection(value)
  case value
    when String then value.split(COLLECTION_SEPARATOR)
    when Hash then value.values.first.scan(/\[(.*?)\]/).flatten
    when NilClass then []
    when Array then value
    else [value]
  end
end

+ (Object) string_to_binary(value)

Used to convert from Strings to BLOBs



357
358
359
# File 'lib/big_record/connection_adapters/column.rb', line 357

def string_to_binary(value)
  value
end

+ (Object) string_to_date(string)



366
367
368
369
370
371
372
373
374
# File 'lib/big_record/connection_adapters/column.rb', line 366

def string_to_date(string)
  # Important: the stored value could be an instance of Time.  If we don't
  #   cast to Date, the cast will fail because column type != value type
  return new_date(string.year, string.month, string.day) if (string.is_a?Time)
  return string unless string.is_a?(String)
  return nil if string.empty?

  fast_string_to_date(string) || fallback_string_to_date(string)
end

+ (Object) string_to_dummy_time(string)



383
384
385
386
387
388
# File 'lib/big_record/connection_adapters/column.rb', line 383

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

  string_to_time "2000-01-01 #{string}"
end

+ (Object) string_to_time(string)



376
377
378
379
380
381
# File 'lib/big_record/connection_adapters/column.rb', line 376

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

+ (Object) value_to_boolean(value)

convert something to a boolean



391
392
393
394
395
396
397
# File 'lib/big_record/connection_adapters/column.rb', line 391

def value_to_boolean(value)
  if value == true || value == false
    value
  else
    %w(true t 1).include?(value.to_s.downcase)
  end
end

+ (Object) value_to_decimal(value)

convert something to a BigDecimal



400
401
402
403
404
405
406
407
408
# File 'lib/big_record/connection_adapters/column.rb', line 400

def value_to_decimal(value)
  if value.is_a?(BigDecimal)
    value
  elsif value.respond_to?(:to_d)
    value.to_d
  else
    value.to_s.to_d
  end
end

Instance Method Details

- (Boolean) collection?

Returns:

  • (Boolean)


101
102
103
# File 'lib/big_record/connection_adapters/column.rb', line 101

def collection?
  @collection
end

- (Object) family



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

def family
  self.class.extract_family(self.name)
end

- (Boolean) family?

Returns:

  • (Boolean)


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

def family?
  name =~ /:\Z/
end

- (Object) hash_collection_to_embedded_collection(hash_collection)



221
222
223
224
225
226
227
# File 'lib/big_record/connection_adapters/column.rb', line 221

def hash_collection_to_embedded_collection(hash_collection)
  return hash_collection unless hash_collection.is_a?(Array)
  hash_collection.each_with_index do |hash, i|
    hash_collection[i] = hash_to_embedded(hash) if hash.is_a?(Hash)
  end
  hash_collection
end

- (Object) hash_to_embedded(value)



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

def hash_to_embedded(value)
  case value
    when BigRecord::Embedded then value
    when Hash then self.klass.instantiate(value)
  end
end

- (Object) hash_to_embedded_collection(hash)



216
217
218
219
# File 'lib/big_record/connection_adapters/column.rb', line 216

def hash_to_embedded_collection(hash)
  hash_collection = hash.is_a?(Hash) ? self.class.hash_to_collection(hash) : hash
  hash_collection_to_embedded_collection(hash_collection)
end

- (Object) human_name

Returns the human name of the column name.

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


212
213
214
# File 'lib/big_record/connection_adapters/column.rb', line 212

def human_name
  Base.human_attribute_name(@name)
end

- (Object) klass

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



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/big_record/connection_adapters/column.rb', line 106

def klass
  @klass ||=
  case type
    when :integer       then Fixnum
    when :float         then Float
    when :decimal       then BigDecimal
    when :datetime      then Time
    when :date          then Date
    when :timestamp     then Time
    when :time          then Time
    when :text, :string then String
    when :binary        then String
    when :boolean       then Object
    when :map           then Hash
    when :object        then Object
    else type.to_s.constantize
  end
end

- (Boolean) number?

Returns:

  • (Boolean)


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

def number?
  [:float, :integer, :decimal].include? type
end

- (Object) preinitialize(value)

callback may be implemented by subclasses if value needs to be ‘massaged’ before instantiation.



69
70
71
# File 'lib/big_record/connection_adapters/column.rb', line 69

def preinitialize(value)
  # do something to a column value's attributes before it is instantiated.
end

- (Boolean) primitive?

Returns:

  • (Boolean)


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

def primitive?
  @primitive ||= ([:integer, :float, :decimal, :datetime, :date, :timestamp, :time, :text, :string, :binary, :boolean, :map, :object].include? type)
end

- (Object) qualifier



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

def qualifier
  self.class.extract_qualifier(self.name)
end

- (Boolean) text?

Returns:

  • (Boolean)


77
78
79
# File 'lib/big_record/connection_adapters/column.rb', line 77

def text?
  [:string, :text].include? type
end

- (Object) type_cast(value)

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



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
163
164
165
166
167
168
# File 'lib/big_record/connection_adapters/column.rb', line 126

def type_cast(value)
  # FIXME: this should be recursive but it doesn't work with type_cast_code()... why does
  # ActiveRecord use type_cast_code ???
  if collection?
    return [] if value.nil?
    case type
      when :string    then self.class.hash_to_string_collection(value)
      when :text      then self.class.hash_to_string_collection(value)
      when :integer   then self.class.hash_to_integer_collection(value)
      when :float     then self.class.hash_to_float_collection(value)
      when :decimal   then self.class.hash_to_decimal_collection(value)
      when :datetime  then self.class.hash_to_time_collection(value)
      when :timestamp then self.class.hash_to_time_collection(value)
      when :time      then self.class.hash_to_dummy_time_collection(value)
      when :date      then self.class.hash_to_date_collection(value)
      when :binary    then self.class.hash_to_string_collection(value)
      when :boolean   then self.class.hash_to_boolean_collection(value)
      when :map       then value
      when :object    then value
      else hash_to_embedded_collection(value)
    end
  else
    casted_value =
    case type
      when :string    then value
      when :text      then value
      when :integer   then value.to_i rescue value ? 1 : 0
      when :float     then value.to_f rescue value ? 1.0 : 0.0
      when :decimal   then self.class.value_to_decimal(value)
      when :datetime  then self.class.string_to_time(value)
      when :timestamp then self.class.string_to_time(value)
      when :time      then self.class.string_to_dummy_time(value)
      when :date      then self.class.string_to_date(value)
      when :binary    then self.class.binary_to_string(value)
      when :boolean   then self.class.value_to_boolean(value)
      when :map       then value
      when :object    then value
      else hash_to_embedded(value)
    end
    # Make sure that the returned value matches the current schema.
    casted_value.is_a?(klass) ? casted_value : nil
  end
end

- (Object) type_cast_code(var_name)



170
171
172
173
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
# File 'lib/big_record/connection_adapters/column.rb', line 170

def type_cast_code(var_name)
  if collection?
    case type
      when :string    then "#{self.class.name}.hash_to_string_collection(#{var_name})"
      when :text      then "#{self.class.name}.hash_to_string_collection(#{var_name})"
      when :integer   then "#{self.class.name}.hash_to_integer_collection(#{var_name})"
      when :float     then "#{self.class.name}.hash_to_float_collection(#{var_name})"
      when :decimal   then "#{self.class.name}.hash_to_decimal_collection(#{var_name})"
      when :datetime  then "#{self.class.name}.hash_to_time_collection(#{var_name})"
      when :timestamp then "#{self.class.name}.hash_to_time_collection(#{var_name})"
      when :time      then "#{self.class.name}.hash_to_dummy_time_collection(#{var_name})"
      when :date      then "#{self.class.name}.hash_to_date_collection(#{var_name})"
      when :binary    then "#{self.class.name}.hash_to_string_collection(#{var_name})"
      when :boolean   then "#{self.class.name}.hash_to_boolean_collection(#{var_name})"
      when :map       then nil
      when :object    then nil
      else nil
    end
  else
    case type
      when :string    then nil
      when :text      then nil
      when :integer   then "(#{var_name}.to_i rescue #{var_name} ? 1 : 0)"
      when :float     then "#{var_name}.to_f"
      when :decimal   then "#{self.class.name}.value_to_decimal(#{var_name})"
      when :datetime  then "#{self.class.name}.string_to_time(#{var_name})"
      when :timestamp then "#{self.class.name}.string_to_time(#{var_name})"
      when :time      then "#{self.class.name}.value_to_dummy_time(#{var_name})"
      when :date      then "#{self.class.name}.string_to_date(#{var_name})"
      when :binary    then "#{self.class.name}.binary_to_string(#{var_name})"
      when :boolean   then "#{self.class.name}.value_to_boolean(#{var_name})"
      when :map       then nil
      when :object    then nil
      else nil
    end
  end
end