Class: ActiveRecord::HyperBase

Inherits:
Base
  • Object
show all
Defined in:
lib/hyper_record.rb

Constant Summary collapse

BILLION =
1_000_000_000.0
ROW_KEY_OFFSET =
0
COLUMN_FAMILY_OFFSET =
1
COLUMN_QUALIFIER_OFFSET =
2
VALUE_OFFSET =
3
TIMESTAMP_OFFSET =
4

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

hypertable_connection, inherited, require_hypertable_thrift_client

Constructor Details

#initialize(attrs = {}) ⇒ HyperBase

Returns a new instance of HyperBase.



31
32
33
34
# File 'lib/hyper_record.rb', line 31

def initialize(attrs={})
  super(attrs)
  self.ROW = attrs[:ROW] if attrs[:ROW] && attrs[:ROW]
end

Class Attribute Details

.mutatorObject

Returns the value of attribute mutator.



574
575
576
# File 'lib/hyper_record.rb', line 574

def mutator
  @mutator
end

.mutator_flagsObject

Returns the value of attribute mutator_flags.



574
575
576
# File 'lib/hyper_record.rb', line 574

def mutator_flags
  @mutator_flags
end

.mutator_flush_intervalObject

Returns the value of attribute mutator_flush_interval.



574
575
576
# File 'lib/hyper_record.rb', line 574

def mutator_flush_interval
  @mutator_flush_interval
end

.qualified_columnsObject

qualified_column :misc, :qualifiers => [:name, :url]



524
525
526
# File 'lib/hyper_record.rb', line 524

def qualified_columns
  @qualified_columns
end

.row_key_attributes(*attrs) ⇒ Object

row_key_attributes :regex => /_(\d4-\d2-\d2_\d2:\d2)$/, :attribute_names => [:timestamp]



538
539
540
# File 'lib/hyper_record.rb', line 538

def row_key_attributes
  @row_key_attributes
end

Class Method Details

.abstract_class?Boolean

Returns:

  • (Boolean)


208
209
210
# File 'lib/hyper_record.rb', line 208

def abstract_class?
  self == ActiveRecord::HyperBase
end

.close_mutator(mutator, flush = 0) ⇒ Object

As of Hypertable 0.9.2.5, flush is automatically performed on a close_mutator call (so flush should default to 0).



593
594
595
# File 'lib/hyper_record.rb', line 593

def close_mutator(mutator, flush=0)
  self.connection.close_mutator(mutator, flush)
end

.close_scanner(scanner) ⇒ Object



606
607
608
# File 'lib/hyper_record.rb', line 606

def close_scanner(scanner)
  self.connection.close_scanner(scanner)
end

.column_families_without_row_keyObject



508
509
510
# File 'lib/hyper_record.rb', line 508

def column_families_without_row_key
  columns[1,columns.length]
end

.columnsObject

Returns array of column objects for table associated with this class.



481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
# File 'lib/hyper_record.rb', line 481

def columns
  unless @columns
    @columns = connection.columns(table_name, "#{name} Columns")
    @qualified_columns ||= []
    @qualified_columns.each{|qc|
      # Remove the column family from the column list
      @columns = @columns.reject{|c| c.name == qc[:column_name].to_s}
      connection.remove_column_from_name_map(table_name, qc[:column_name].to_s)

      # Add the new qualified column family to the column list
      @columns << connection.add_qualified_column(table_name, qc[:column_name].to_s, qc[:qualifiers])
    }
    @columns.each {|column| column.primary = column.name == primary_key}
  end
  @columns
end

.convert_cells_to_hashes(cells, options = {}) ⇒ Object

Converts cells that come back from Hypertable into hashes. Each hash represents a separate record (where each cell that has the same row key is considered one record).



366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
# File 'lib/hyper_record.rb', line 366

def convert_cells_to_hashes(cells, options={})
  rows = []
  current_row = {}

  # Cells are guaranteed to come back in row key order, so assemble
  # a row by iterating over each cell and checking to see if the row key
  # has changed.  If it has, then the row is complete and needs to be
  # instantiated before processing the next cell.
  cells.each_with_index do |cell, i|
    current_row['ROW'] = cell[ROW_KEY_OFFSET]

    family = connection.rubify_column_name(cell[COLUMN_FAMILY_OFFSET])

    if !cell[COLUMN_QUALIFIER_OFFSET].blank?
      current_row[family] ||= {}
      current_row[family][cell[COLUMN_QUALIFIER_OFFSET]] = cell[VALUE_OFFSET]
    else
      current_row[family] = cell[VALUE_OFFSET]
    end

    # Instantiate the row if we've processed all cells for the row
    next_index = i + 1

    # Check to see if next cell has different row key or if we're at
    # the end of the cell stream.
    if (cells[next_index] and cells[next_index][ROW_KEY_OFFSET] != current_row['ROW']) or next_index >= cells.length
      # Make sure that the resulting object has attributes for all
      # columns - even ones that aren't in the response (due to limited
      # select)

      for col in column_families_without_row_key
        next if options[:instantiate_only_requested_columns] && !options[:select].include?(col.name)

        if !current_row.has_key?(col.name)
          if col.is_a?(ActiveRecord::ConnectionAdapters::QualifiedColumn)
            current_row[col.name] = {}
          else
            current_row[col.name] = nil
          end
        end
      end

      rows << current_row
      current_row = {}
    end
  end

  rows
end

.convert_cells_to_instantiated_rows(cells, options = {}) ⇒ Object



416
417
418
# File 'lib/hyper_record.rb', line 416

def convert_cells_to_instantiated_rows(cells, options={})
  convert_cells_to_hashes(cells, options).map{|row| instantiate(row)}
end

.delete(*ids) ⇒ Object



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

def delete(*ids)
  self.connection.delete_rows(table_name, ids.flatten)
end

.drop_tableObject



469
470
471
# File 'lib/hyper_record.rb', line 469

def drop_table
  connection.drop_table(table_name) if table_exists?
end

.each_cell(scanner, &block) ⇒ Object

Iterator methods



615
616
617
# File 'lib/hyper_record.rb', line 615

def each_cell(scanner, &block)
  self.connection.each_cell(scanner, &block)
end

.each_cell_as_arrays(scanner, &block) ⇒ Object



619
620
621
# File 'lib/hyper_record.rb', line 619

def each_cell_as_arrays(scanner, &block)
  self.connection.each_cell_as_arrays(scanner, &block)
end

.each_row(scanner, &block) ⇒ Object



623
624
625
# File 'lib/hyper_record.rb', line 623

def each_row(scanner, &block)
  self.connection.each_row(scanner, &block)
end

.each_row_as_arrays(scanner, &block) ⇒ Object



627
628
629
# File 'lib/hyper_record.rb', line 627

def each_row_as_arrays(scanner, &block)
  self.connection.each_row_as_arrays(scanner, &block)
end

.exists?(id_or_conditions) ⇒ Boolean

Returns:

  • (Boolean)


212
213
214
215
216
217
218
219
220
221
# File 'lib/hyper_record.rb', line 212

def exists?(id_or_conditions)
  case id_or_conditions
    when Fixnum, String
      !find(:first, :row_keys => [id_or_conditions]).nil?
    when Hash
      !find(:first, :conditions => id_or_conditions).nil?
    else
      raise "only Fixnum, String and Hash arguments supported"
  end
end

.find(*args) ⇒ Object



227
228
229
230
231
232
233
234
235
# File 'lib/hyper_record.rb', line 227

def find(*args)
  options = args.extract_options!

  case args.first
    when :first then find_initial(options)
    when :all   then find_by_options(options)
    else             find_from_ids(args, options)
  end
end

.find_by_hql(hql) ⇒ Object Also known as: find_by_sql

Return the records that match a specific HQL query.



421
422
423
424
425
426
427
# File 'lib/hyper_record.rb', line 421

def find_by_hql(hql)
  hql_result = connection.execute(hql)
  cells_in_native_array_format = hql_result.cells.map do |c| 
    connection.cell_native_array(c.row_key, c.column_family, c.column_qualifier, c.value)
  end
  convert_cells_to_instantiated_rows(cells_in_native_array_format)
end

.find_by_options(options) ⇒ Object

Return an array of records matching the finder options.



332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/hyper_record.rb', line 332

def find_by_options(options)
  set_default_options(options)

  # If requested, instead of retrieving the matching cells from
  # Hypertable, simply return a scan spec that matches the finder
  # options.
  if options[:scan_spec]
    return connection.convert_options_to_scan_spec(options)
  end

  cells = connection.execute_with_options(options)

  if HyperBase.log_calls
    msg = [ "Select" ]
    for key in options.keys
      case key
        when :columns
          msg << "  columns\t#{options[:columns].map{|c| c.name}.join(',')}"
        else
          msg << "  #{key}\t#{options[key]}"
      end
    end
    msg << "Returned #{cell_count} cells"

    RAILS_DEFAULT_LOGGER.info(msg)
    # puts msg
  end

  convert_cells_to_instantiated_rows(cells, options)
end

.find_each_row(*args) ⇒ Object

Returns each row matching the finder options as a HyperRecord object. Each object is yielded to the caller so that large queries can be processed one object at a time without pulling the entire result set into memory.

Page.find_each_row(:all) do |page|

...

end



272
273
274
275
276
# File 'lib/hyper_record.rb', line 272

def find_each_row(*args)
  find_each_row_as_arrays(*args) do |row|
    yield convert_cells_to_instantiated_rows(row).first
  end
end

.find_each_row_as_arrays(*args) ⇒ Object

Returns each row matching the finder options as an array of cells in native array format. Each row is yielded to the caller so that large queries can be processed one row at a time without pulling the entire result set into memory.

Page.find_each_row(:all) do |page_as_array_of_cells|

...

end



286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'lib/hyper_record.rb', line 286

def find_each_row_as_arrays(*args)
  scan_spec = find_to_scan_spec(*args)
  with_scanner(scan_spec) do |scanner|
    row = []
    current_row_key = nil

    each_cell_as_arrays(scanner) do |cell|
      current_row_key ||= cell[ROW_KEY_OFFSET]

      if cell[ROW_KEY_OFFSET] == current_row_key
        row << cell
      else
        yield row
        row = [cell]
        current_row_key = cell[ROW_KEY_OFFSET]
      end
    end 

    yield row unless row.empty?
  end
end

.find_from_ids(ids, options) ⇒ Object

Return multiple records by row keys.



431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
# File 'lib/hyper_record.rb', line 431

def find_from_ids(ids, options)
  expects_array = ids.first.kind_of?(Array)
  return ids.first if expects_array && ids.first.empty?
  ids = ids.flatten.compact.uniq

  case ids.size
    when 0
      raise RecordNotFound, "Couldn't find #{name} without an ID"
    when 1
      result = find_one(ids.first, options)
      expects_array ? [ result ] : result
    else
      find_some(ids, options)
  end
end

.find_initial(options) ⇒ Object

Return the first record that matches the finder options.



321
322
323
324
325
326
327
328
329
# File 'lib/hyper_record.rb', line 321

def find_initial(options)
  options.update(:limit => 1)

  if options[:scan_spec]
    find_by_options(options)
  else
    find_by_options(options).first
  end
end

.find_one(id, options) ⇒ Object

Return a single record identified by a row key.



448
449
450
451
452
453
454
455
456
457
458
# File 'lib/hyper_record.rb', line 448

def find_one(id, options)
  return nil if id.blank?

  options[:row_keys] = [id.to_s]

  if result = find_initial(options)
    result
  else
    raise ::ActiveRecord::RecordNotFound, "Couldn't find #{name} with ID=#{id}"
  end
end

.find_some(ids, options) ⇒ Object



460
461
462
463
# File 'lib/hyper_record.rb', line 460

def find_some(ids, options)
  options[:row_keys] = [ids.map{|i| i.to_s}]
  find_by_options(options)
end

.find_to_scan_spec(*args) ⇒ Object

Converts incoming finder options into a scan spec. A scan spec is an object used to describe query parameters (columns to retrieve, number of rows to retrieve, row key ranges) for Hypertable queries.



240
241
242
243
244
245
# File 'lib/hyper_record.rb', line 240

def find_to_scan_spec(*args)
  options = args.extract_options!
  options[:scan_spec] = true
  args << options
  find(*args)
end

.find_with_scanner(*args, &block) ⇒ Object

Returns a scanner object that allows you to iterate over the result set using the lower-level Thrift client APIs methods that require a scanner object. e.g.,

Page.find_with_scanner(:all, :limit => 1) do |scanner|

Page.each_cell_as_arrays(scanner) do |cell|
  ...
end

end

See the Thrift Client API documentation for more detail. hypertable.org/thrift-api-ref/index.html



259
260
261
262
# File 'lib/hyper_record.rb', line 259

def find_with_scanner(*args, &block)
  scan_spec = find_to_scan_spec(*args)
  with_scanner(scan_spec, &block)
end

.flush_mutator(mutator) ⇒ Object



597
598
599
# File 'lib/hyper_record.rb', line 597

def flush_mutator(mutator)
  self.connection.flush_mutator(mutator)
end

.mutator_options(*attrs) ⇒ Object



575
576
577
578
579
580
581
582
# File 'lib/hyper_record.rb', line 575

def mutator_options(*attrs)
  symbolized_attrs = attrs.first.symbolize_keys
  @mutator_flags = symbolized_attrs[:flags].to_i
  @mutator_flush_interval = symbolized_attrs[:flush_interval].to_i
  if symbolized_attrs[:persistent]
    @mutator = self.open_mutator(@mutator_flags, @mutator_flush_interval)
  end
end

.open_mutator(flags = @mutator_flags.to_i, flush_interval = @mutator_flush_interval.to_i) ⇒ Object

Return an open mutator on this table.



587
588
589
# File 'lib/hyper_record.rb', line 587

def open_mutator(flags=@mutator_flags.to_i, flush_interval=@mutator_flush_interval.to_i)
  self.connection.open_mutator(table_name, flags, flush_interval)
end

.open_scanner(scan_spec) ⇒ Object

Scanner methods



602
603
604
# File 'lib/hyper_record.rb', line 602

def open_scanner(scan_spec)
  self.connection.open_scanner(self.table_name, scan_spec)
end

.primary_keyObject

Returns the primary key field for a table. In Hypertable, a single row key exists for each row. The row key is referred to as ROW in HQL, so we’ll refer to it the same way here.



476
477
478
# File 'lib/hyper_record.rb', line 476

def primary_key
  "ROW"
end

.qualified?(column_name) ⇒ Boolean

Returns:

  • (Boolean)


498
499
500
# File 'lib/hyper_record.rb', line 498

def qualified?(column_name)
  @qualified_columns.map{|c| c[:column_name]}.include?(column_name.to_sym)
end

.qualified_column(*attrs) ⇒ Object



525
526
527
528
529
530
531
532
533
534
535
# File 'lib/hyper_record.rb', line 525

def qualified_column(*attrs)
  @qualified_columns ||= []
  name = attrs.shift

  qualifiers = attrs.shift
  qualifiers = qualifiers.symbolize_keys[:qualifiers] if qualifiers
  @qualified_columns << {
    :column_name => name,
    :qualifiers => qualifiers || []
  }
end

.qualified_column_names_without_row_keyObject



512
513
514
515
516
517
518
519
520
521
# File 'lib/hyper_record.rb', line 512

def qualified_column_names_without_row_key
  cols = column_families_without_row_key.map{|c| c.name}
  for qc in @qualified_columns
    cols.delete(qc[:column_name].to_s)
    for qualifier in qc[:qualifiers]
      cols << "#{qc[:column_name]}:#{qualifier}"
    end
  end
  cols
end

.quoted_column_names(attributes = attributes_with_quotes) ⇒ Object



502
503
504
505
506
# File 'lib/hyper_record.rb', line 502

def quoted_column_names(attributes=attributes_with_quotes)
  attributes.keys.collect do |column_name|
    self.class.connection.quote_column_name_for_table(column_name, table_name)
  end
end

.set_default_options(options) ⇒ Object

Each hypertable query requires some default options (e.g., table name) that are set here if not specified in the query.



310
311
312
313
314
315
316
317
318
# File 'lib/hyper_record.rb', line 310

def set_default_options(options)
  options[:table_name] ||= table_name
  options[:columns] ||= columns

  # Don't request the ROW key explicitly, it always comes back
  options[:select] ||= qualified_column_names_without_row_key.map{|c| 
    connection.hypertable_column_name(c, table_name)
  }
end

.table_exists?(name = table_name) ⇒ Boolean

Returns:

  • (Boolean)


465
466
467
# File 'lib/hyper_record.rb', line 465

def table_exists?(name=table_name)
  connection.tables.include?(name)
end

.with_scanner(scan_spec, &block) ⇒ Object



610
611
612
# File 'lib/hyper_record.rb', line 610

def with_scanner(scan_spec, &block)
  self.connection.with_scanner(self.table_name, scan_spec, &block)
end

.with_thrift_client(&block) ⇒ Object



631
632
633
# File 'lib/hyper_record.rb', line 631

def with_thrift_client(&block)
  self.connection.raw_thrift_client(&block)
end

Instance Method Details

#attributes_with_quotes(include_primary_key = true, include_readonly_attributes = true) ⇒ Object

Returns a copy of the attributes hash where all the values have been safely quoted for insertion. Translates qualified columns from a Hash value in Ruby to a flat list of attributes.

>
"ROW" => "page_1",
"name" => "name",
"url" => "http://www.icanhascheezburger.com"



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/hyper_record.rb', line 135

def attributes_with_quotes(include_primary_key = true, include_readonly_attributes = true)
  quoted = attributes.inject({}) do |quoted, (name, value)|
    if column = column_for_attribute(name)
      if column.is_a?(ConnectionAdapters::QualifiedColumn) and value.is_a?(Hash)
        value.keys.each{|k|
          quoted[self.class.connection.qualified_column_name(column.name, k)] = quote_value(value[k], column)
        }
      else
        quoted[name] = quote_value(value, column) unless !include_primary_key && column.primary
      end
    end
    quoted
  end
  include_readonly_attributes ? quoted : remove_readonly_attributes(quoted)
end

#create(mutator = self.class.mutator) ⇒ Object



43
44
45
46
47
48
# File 'lib/hyper_record.rb', line 43

def create(mutator=self.class.mutator)
  write_quoted_attributes(attributes_with_quotes(false, false), 
    self.class.table_name, mutator)
  @new_record = false
  self.attributes[self.class.primary_key]
end

#create_or_update_with_mutator(mutator) ⇒ Object

Raises:

  • (ReadOnlyRecord)


76
77
78
79
80
# File 'lib/hyper_record.rb', line 76

def create_or_update_with_mutator(mutator)
  raise ReadOnlyRecord if readonly?
  result = new_record? ? create(mutator) : update(mutator)
  result != false
end

#decrement(attribute, by = 1) ⇒ Object



118
119
120
# File 'lib/hyper_record.rb', line 118

def decrement(attribute, by=1)
  increment(attribute, -by)
end

#decrement!(attribute, by = 1) ⇒ Object



122
123
124
# File 'lib/hyper_record.rb', line 122

def decrement!(attribute, by=1)
  increment!(attribute, -by)
end

#delete_cells(cells, table = self.class.table_name) ⇒ Object

Delete an array of cells from Hypertable cells is an array of cell keys [[“row”, “column”], …]



187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/hyper_record.rb', line 187

def delete_cells(cells, table=self.class.table_name)
  if HyperBase.log_calls
    msg = [
      "Deleting #{cells.length} cells from #{table} table",
      cells.map{|c| [ c[0], c[1] ].compact.join("\t")}
    ].join("\n")
    RAILS_DEFAULT_LOGGER.info(msg)
    # puts msg
  end

  connection.delete_cells(table, cells)
end

#delete_rows(row_keys, table = self.class.table_name) ⇒ Object

Delete an array of rows from Hypertable rows is an array of row keys [“row1”, “row2”, …]



202
203
204
# File 'lib/hyper_record.rb', line 202

def delete_rows(row_keys, table=self.class.table_name)
  connection.delete_rows(table, cells)
end

#destroyObject

Destroy an object. Since Hypertable does not have foreign keys, association cells must be removed manually.



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/hyper_record.rb', line 84

def destroy
  # check for associations and delete association cells as necessary
  for reflection_key in self.class.reflections.keys
    case self.class.reflections[reflection_key].macro
      when :has_and_belongs_to_many
        # remove all the association cells from the associated objects
        cells_to_delete = []

        for row_key in self.send(self.class.reflections[reflection_key].association_foreign_key).keys
          cells_to_delete << connection.cell_native_array(row_key, self.class.reflections[reflection_key].primary_key_name, self.ROW)
        end

        self.delete_cells(cells_to_delete, self.class.reflections[reflection_key].klass.table_name)
    end
  end

  self.class.connection.delete_rows(self.class.table_name, [self.ROW])
end

#increment(attribute, by = 1) ⇒ Object

Casts the attribute to an integer before performing the increment. This is necessary because Hypertable only supports integer types at the moment. The cast has the effect of initializing nil values (and most string values) to zero.



107
108
109
110
111
# File 'lib/hyper_record.rb', line 107

def increment(attribute, by=1)
  self[attribute] = self[attribute].to_i
  self[attribute] += by
  self
end

#increment!(attribute, by = 1) ⇒ Object



113
114
115
116
# File 'lib/hyper_record.rb', line 113

def increment!(attribute, by=1)
  increment(attribute, by)
  self.save
end

#quoted_attributes_to_cells(quoted_attrs, table = self.class.table_name) ⇒ Object

Translates the output of attributes_with_quotes into an array of cells suitable for writing into Hypertable (through the write_cells method). Data format is native array format for cells. [

["row_key", "column_family", "column_qualifier", "value"],

]



157
158
159
160
161
162
163
164
165
# File 'lib/hyper_record.rb', line 157

def quoted_attributes_to_cells(quoted_attrs, table=self.class.table_name)
  cells = []
  pk = self.attributes[self.class.primary_key]
  quoted_attrs.keys.each{|key|
    name, qualifier = connection.hypertable_column_name(key, table).split(':', 2)
    cells << connection.cell_native_array(pk, name, qualifier, quoted_attrs[key])
  }
  cells
end

#save_with_mutator(mutator) ⇒ Object

Allows the save operation to be performed with a specific mutator. By default, a new mutator is opened, flushed and closed for each save operation. Write-heavy application may wish to manually manage mutator flushes (which happens when the mutator is closed) at the application-layer in order to increase write throughput.

m = Page.open_mutator

p1 = Page.new({:ROW => 'created_with_mutator_1', :url => 'url_1'})
p1.save_with_mutator!(m)
p2 = Page.new({:ROW => 'created_with_mutator_2', :url => 'url_2'})
p2.save_with_mutator!(m)

Page.close_mutator(m)

Future versions of hypertable will provide a mutator that automatically periodically flushes. This feature is expected by Summary 2009. At that time, manually managing the mutator at the



68
69
70
# File 'lib/hyper_record.rb', line 68

def save_with_mutator(mutator)
  create_or_update_with_mutator(mutator)
end

#save_with_mutator!(mutator) ⇒ Object



72
73
74
# File 'lib/hyper_record.rb', line 72

def save_with_mutator!(mutator)
  create_or_update_with_mutator(mutator) || raise(RecordNotSaved)
end

#update(mutator = self.class.mutator) ⇒ Object

Instance Methods



37
38
39
40
41
# File 'lib/hyper_record.rb', line 37

def update(mutator=self.class.mutator)
  write_quoted_attributes(attributes_with_quotes(false, false),
    self.class.table_name, mutator)
  true
end

#write_cells(cells, table = self.class.table_name, mutator = self.class.mutator) ⇒ Object

Write an array of cells to Hypertable



172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/hyper_record.rb', line 172

def write_cells(cells, table=self.class.table_name, mutator=self.class.mutator)
  if HyperBase.log_calls
    msg = [
      "Writing #{cells.length} cells to #{table} table",
      cells.map{|c| [c[0], c[1], c[2], c[3].to_s.first(20)].compact.join("\t")}
    ].join("\n")
    RAILS_DEFAULT_LOGGER.info(msg)
    # puts msg
  end

  connection.write_cells(table, cells, mutator, self.class.mutator_flags, self.class.mutator_flush_interval)
end

#write_quoted_attributes(quoted_attrs, table = self.class.table_name, mutator = self.class.mutator) ⇒ Object



167
168
169
# File 'lib/hyper_record.rb', line 167

def write_quoted_attributes(quoted_attrs, table=self.class.table_name, mutator=self.class.mutator)
  write_cells(quoted_attributes_to_cells(quoted_attrs, table), table, mutator)
end