class BigRecord::Model

Constants

ID_FIELD_SEPARATOR

Constants for special characters in generated IDs. An ID might then look like this: 'United_States-Hawaii-Oahu-Honolulu-b9cef848-a4e0-11dc-a7ba-0018f3137ea8'

ID_WHITE_SPACE_CHAR

Public Class Methods

===(object) click to toggle source

Overwrite the default class equality method to provide support for association proxies.

# File lib/big_record/model.rb, line 822
def ===(object)
  object.is_a?(self)
end
alias_attribute(alias_name, fully_qualified_name) click to toggle source

Define aliases to the fully qualified attributes

# File lib/big_record/model.rb, line 910
def alias_attribute(alias_name, fully_qualified_name)
  self.class_eval <<-EOF
    def #{alias_name}
      read_attribute("#{fully_qualified_name}")
    end
    def #{alias_name}=(value)
      write_attribute("#{fully_qualified_name}", value)
    end
  EOF
end
attr_accessible(*attributes) click to toggle source

Specifies a white list of model attributes that can be set via mass-assignment, such as new(attributes), update_attributes(attributes), or attributes=(attributes)

This is the opposite of the attr_protected macro: Mass-assignment will only set attributes in this list, to assign to the rest of attributes you can use direct writer methods. This is meant to protect sensitive attributes from being overwritten by malicious users tampering with URLs or forms. If you'd rather start from an all-open default and restrict attributes as needed, have a look at attr_protected.

class Customer < ActiveRecord::Base
  attr_accessible :name, :nickname
end

customer = Customer.new(:name => "David", :nickname => "Dave", :credit_rating => "Excellent")
customer.credit_rating # => nil
customer.attributes = { :name => "Jolly fellow", :credit_rating => "Superb" }
customer.credit_rating # => nil

customer.credit_rating = "Average"
customer.credit_rating # => "Average"
# File lib/big_record/model.rb, line 1004
def attr_accessible(*attributes)
  write_inheritable_attribute(:attr_accessible, Set.new(attributes.map(&:to_s)) + (accessible_attributes || []))
end
attr_create_accessible(*attributes) click to toggle source

Attributes listed as create_accessible work with mass assignment ONLY on creation. After that, any updates to that attribute will be protected from mass assignment. This differs from ::attr_readonly since that macro prevents attributes from ever being changed (even with the explicit setters) after the record is created.

class Customer < BigRecord::Base
  attr_create_accessible :name
end

customer = Customer.new(:name => "Greg")
customer.name # => "Greg"
customer.save # => true

customer.attributes = { :name => "Nerd" }
customer.name # => "Greg"
customer.name = "Nerd"
customer.name # => "Nerd"
# File lib/big_record/model.rb, line 1040
def attr_create_accessible(*attributes)
 write_inheritable_attribute(:attr_create_accessible, Set.new(attributes.map(&:to_s)) + (create_accessible_attributes || []))
end
attr_protected(*attributes) click to toggle source

Attributes named in this macro are protected from mass-assignment, such as new(attributes), update_attributes(attributes), or attributes=(attributes).

Mass-assignment to these attributes will simply be ignored, to assign to them you can use direct writer methods. This is meant to protect sensitive attributes from being overwritten by malicious users tampering with URLs or forms.

class Customer < ActiveRecord::Base
  attr_protected :credit_rating
end

customer = Customer.new("name" => David, "credit_rating" => "Excellent")
customer.credit_rating # => nil
customer.attributes = { "description" => "Jolly fellow", "credit_rating" => "Superb" }
customer.credit_rating # => nil

customer.credit_rating = "Average"
customer.credit_rating # => "Average"

To start from an all-closed default and enable attributes as needed, have a look at attr_accessible.

# File lib/big_record/model.rb, line 971
def attr_protected(*attributes)
  write_inheritable_attribute(:attr_protected, Set.new(attributes.map {|a| a.to_s}) + (protected_attributes || []))
end
attr_readonly(*attributes) click to toggle source

Attributes listed as readonly can be set for a new record, but will be ignored in database updates afterwards.

# File lib/big_record/model.rb, line 1014
def attr_readonly(*attributes)
 write_inheritable_attribute(:attr_readonly, Set.new(attributes.map(&:to_s)) + (readonly_attributes || []))
end
base_class() click to toggle source
# File lib/big_record/model.rb, line 826
def base_class
  raise NotImplemented
end
benchmark(title, log_level = Logger::DEBUG, use_silence = true) { || ... } click to toggle source

Log and benchmark multiple statements in a single block.

@example

Project.benchmark("Creating project") do
  project = Project.create("name" => "stuff")
  project.create_manager("name" => "David")
  project.milestones << Milestone.find(:all)
end

The benchmark is only recorded if the current level of the logger matches the log_level, which makes it easy to include benchmarking statements in production software that will remain inexpensive because the benchmark will only be conducted if the log level is low enough.

The logging of the multiple statements is turned off unless use_silence is set to false.

# File lib/big_record/model.rb, line 802
def benchmark(title, log_level = Logger::DEBUG, use_silence = true)
  if logger && logger.level == log_level
    result = nil
    seconds = Benchmark.realtime { result = use_silence ? silence { yield } : yield }
    logger.add(log_level, "#{title} (#{'%.5f' % seconds})")
    result
  else
    yield
  end
end
column(name, type, options={}) click to toggle source

Macro for defining a new column for a model. Invokes {create_column} and adds the new column into the model's column hash.

@param type [Symbol, String] Column type as defined in the source of {ConnectionAdapters::Column#klass} @option options [true,false] :collection Whether this column is a collection. @option options [String] :alias Define an alias for the column that cannot be inferred. By default, 'attribute:name' will be aliased to 'name'. @option options [String] :default Default value to set for this column.

@return [ConnectionAdapters::Column] The column object created.

# File lib/big_record/model.rb, line 892
def column(name, type, options={})
  name = name.to_s
  name = "#{self.default_column_prefix}#{name}" unless (name =~ /:/) || self.default_column_prefix.blank?

  @columns_hash = default_columns unless @columns_hash

  # The other variables that are cached and depend on @columns_hash need to be reloaded
  invalidate_columns

  c = create_column(name, type, options)
  @columns_hash[c.name] = c

  alias_attribute c.alias, c.name if c.alias

  c
end
column_names() click to toggle source

Returns an array of column names as strings.

# File lib/big_record/model.rb, line 859
def column_names
  @column_names = columns_hash.keys
end
columns() click to toggle source

Override this method in the subclasses to add new columns. This is different from ActiveRecord because the number of columns in an Hbase table is variable.

# File lib/big_record/model.rb, line 832
def columns
  @columns = columns_hash.values
end
columns_hash() click to toggle source

Returns a hash of column objects for the table associated with this class.

# File lib/big_record/model.rb, line 837
def columns_hash
  unless @all_columns_hash
    # add default hbase columns
    @all_columns_hash =
      if self == base_class
        if @columns_hash
          default_columns.merge(@columns_hash)
        else
          @columns_hash = default_columns
        end
      else
        if @columns_hash
          superclass.columns_hash.merge(@columns_hash)
        else
          superclass.columns_hash
        end
      end
  end
  @all_columns_hash
end
content_columns() click to toggle source

Returns an array of column objects where the primary id, all columns ending in “_id” or “_count”, and columns used for single table inheritance have been removed.

# File lib/big_record/model.rb, line 865
def content_columns
  @content_columns ||= columns.reject{|c| c.primary || "id"}
end
create_accessible_attributes() click to toggle source

Returns an array of all the attributes that have been specified as create_accessible.

# File lib/big_record/model.rb, line 1045
def create_accessible_attributes
 read_inheritable_attribute(:attr_create_accessible)
end
default_column_prefix() click to toggle source

Default column prefix used when auto-generating column attribute names

# File lib/big_record/model.rb, line 1050
def default_column_prefix
  ""
end
instantiate(raw_record) click to toggle source

Finder methods must instantiate through this method.

# File lib/big_record/model.rb, line 938
def instantiate(raw_record)
  record = self.allocate
  record.deserialize(raw_record)
  record.preinitialize(raw_record)
  record.instance_variable_set(:@new_record, false)
  record.send("safe_attributes=", raw_record, false)
  record
end
new(attrs = nil) click to toggle source

New objects can be instantiated as either empty (pass no construction parameter) or pre-set with attributes but not yet saved (pass a hash with key names matching the associated table column names). In both instances, valid attribute keys are determined by the column names of the associated table – hence you can't have attributes that aren't part of the table columns.

@param [Hash] Optional hash argument consisting of keys that match the names of the columns, and their values.

# File lib/big_record/model.rb, line 144
def initialize(attrs = nil)
  preinitialize(attrs)
  @attributes = attributes_from_column_definition
  self.attributes = attrs unless attrs.nil?
end
primary_key() click to toggle source

Evaluate the name of the column of the primary key only once

# File lib/big_record/model.rb, line 784
def primary_key
  raise NotImplemented
end
readonly_attributes() click to toggle source

Returns an array of all the attributes that have been specified as readonly.

# File lib/big_record/model.rb, line 1019
def readonly_attributes
 read_inheritable_attribute(:attr_readonly)
end
silence() { || ... } click to toggle source

Silences the logger for the duration of the block.

# File lib/big_record/model.rb, line 814
def silence
  old_logger_level, logger.level = logger.level, Logger::ERROR if logger
  yield
ensure
  logger.level = old_logger_level if logger
end
store_primary_key?() click to toggle source
# File lib/big_record/model.rb, line 91
def self.store_primary_key?
  false
end
undecorated_table_name(class_name = base_class.name) click to toggle source

Guesses the table name, but does not decorate it with prefix and suffix information.

# File lib/big_record/model.rb, line 1092
def undecorated_table_name(class_name = base_class.name)
  table_name = Inflector.underscore(Inflector.demodulize(class_name))
  table_name = Inflector.pluralize(table_name) if pluralize_table_names
  table_name
end

Protected Class Methods

compute_type(type_name) click to toggle source

Returns the class type of the record using the current module as a prefix. So descendents of MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.

# File lib/big_record/model.rb, line 1086
def compute_type(type_name)
  type_name.constantize
end
create_column(name, type, options) click to toggle source

Creates a {ConnectionAdapters::Column} object.

# File lib/big_record/model.rb, line 1067
def create_column(name, type, options)
  ConnectionAdapters::Column.new(name, type, options)
end
default_columns() click to toggle source
# File lib/big_record/model.rb, line 1071
def default_columns
  {}
end
default_views() click to toggle source
# File lib/big_record/model.rb, line 1075
def default_views
  {:all=>ConnectionAdapters::View.new('all', nil, self), :default=>ConnectionAdapters::View.new('default', nil, self)}
end
invalidate_columns() click to toggle source
# File lib/big_record/model.rb, line 1060
def invalidate_columns
  @columns = nil
  @column_names = nil
  @content_columns = nil
end
invalidate_views() click to toggle source
# File lib/big_record/model.rb, line 1055
def invalidate_views
  @views = nil
  @view_names = nil
end

Public Instance Methods

==(comparison_object) click to toggle source

Returns true if the comparison_object is the same object, or is of the same type and has the same id.

# File lib/big_record/model.rb, line 310
def ==(comparison_object)
  comparison_object.equal?(self) ||
    (comparison_object.instance_of?(self.class) &&
      comparison_object.id == id &&
      !comparison_object.new_record?)
end
[](attr_name) click to toggle source

Returns the value of the attribute identified by attr_name after it has been typecast (for example, “2004-12-12” in a data column is cast to a date object, like Date.new(2004, 12, 12)). (Alias for the protected #read_attribute method).

# File lib/big_record/model.rb, line 225
def [](attr_name)
  read_attribute(attr_name)
end
[]=(attr_name, value) click to toggle source

Updates the attribute identified by attr_name with the specified value. (Alias for the protected #write_attribute method).

# File lib/big_record/model.rb, line 231
def []=(attr_name, value)
  write_attribute(attr_name, value)
end
all_attributes_loaded=(loaded) click to toggle source
# File lib/big_record/model.rb, line 255
def all_attributes_loaded=(loaded)
  @all_attributes_loaded = loaded
end
all_attributes_loaded?() click to toggle source
# File lib/big_record/model.rb, line 259
def all_attributes_loaded?
  @all_attributes_loaded
end
attribute_for_inspect(attr_name) click to toggle source

Format attributes nicely for inspect.

# File lib/big_record/model.rb, line 264
def attribute_for_inspect(attr_name)
  value = read_attribute(attr_name)

  if value.is_a?(String) && value.length > 50
    "#{value[0..50]}...".inspect
  elsif value.is_a?(Date) || value.is_a?(Time)
    %Q("#{value.to_s(:db)}")
  else
    value.inspect
  end
end
attribute_names() click to toggle source

Returns an array of names for the attributes available on this object sorted alphabetically.

# File lib/big_record/model.rb, line 295
def attribute_names
  @attributes.keys.sort
end
attribute_present?(attribute) click to toggle source

Returns true if the specified attribute has been set by the user or by a database load and is neither nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings).

@return [String, Symbol] Name of an attribute. @return [true,false] Whether that attribute exists.

# File lib/big_record/model.rb, line 281
def attribute_present?(attribute)
  value = read_attribute(attribute)
  !value.blank? or value == 0
end
attributes() click to toggle source

Get the attributes hash of the object.

@return [Hash] a duplicated attributes hash

# File lib/big_record/model.rb, line 207
def attributes()
  @attributes.dup
end
attributes=(new_attributes, guard_protected_attributes = true) click to toggle source

Allows you to set all the attributes at once by passing in a hash with keys matching the attribute names (which again matches the column names). Sensitive attributes can be protected from this form of mass-assignment by using the attr_protected macro. Or you can alternatively specify which attributes can be accessed in with the attr_accessible macro. Then all the attributes not included in that won't be allowed to be mass-assigned.

# File lib/big_record/model.rb, line 240
def attributes=(new_attributes, guard_protected_attributes = true)
  return if new_attributes.nil?
  attributes = new_attributes.dup
  attributes.stringify_keys!

  multi_parameter_attributes = []
  attributes = remove_attributes_protected_from_mass_assignment(attributes) if guard_protected_attributes

  attributes.each do |k, v|
    k.include?("(") ? multi_parameter_attributes << [ k, v ] : send(k + "=", v)
  end

  assign_multiparameter_attributes(multi_parameter_attributes)
end
column_for_attribute(name) click to toggle source

Overridden by FamilySpanColumns Returns the column object for the named attribute.

# File lib/big_record/model.rb, line 305
def column_for_attribute(name)
  self.class.columns_hash[name.to_s]
end
connection() click to toggle source

Returns the connection adapter of the current session.

# File lib/big_record/model.rb, line 413
def connection
  self.class.connection
end
deserialize(attrs = nil) click to toggle source
# File lib/big_record/model.rb, line 157
def deserialize(attrs = nil)
end
destroy() click to toggle source

Deletes the record in the database and freezes this instance to reflect that no changes should be made (since they can't be persisted).

# File lib/big_record/model.rb, line 390
def destroy
  raise NotImplemented
end
eql?(comparison_object) click to toggle source

Delegates to ==

# File lib/big_record/model.rb, line 318
def eql?(comparison_object)
  self == (comparison_object)
end
freeze() click to toggle source

Just freeze the attributes hash, such that associations are still accessible even on destroyed records.

# File lib/big_record/model.rb, line 350
def freeze
  @attributes.freeze; self
end
frozen?() click to toggle source

Checks whether the object has had its attributes hash frozen.

@return [true,false]

# File lib/big_record/model.rb, line 357
def frozen?
  @attributes.frozen?
end
has_attribute?(attr_name) click to toggle source

Returns true if the given attribute is in the attributes hash

@return [String, Symbol] Name of an attribute. @return [true,false] Whether that attribute exists in the attributes hash.

# File lib/big_record/model.rb, line 290
def has_attribute?(attr_name)
  @attributes.has_key?(attr_name.to_s)
end
hash() click to toggle source

Delegates to id in order to allow two records of the same type and id to work with something like:

[ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
# File lib/big_record/model.rb, line 324
def hash
  id.hash
end
human_attribute_name(attribute_key) click to toggle source
# File lib/big_record/model.rb, line 299
def human_attribute_name(attribute_key)
  self.class.human_attribute_name(attribute_key)
end
id() click to toggle source

A model instance's primary key is always available as model.id whether you name it the default 'id' or set it to something else.

# File lib/big_record/model.rb, line 190
def id
  attr_name = self.class.primary_key
  c = column_for_attribute(attr_name)
  define_read_method(:id, attr_name, c) if self.class.generate_read_methods
  read_attribute(attr_name)
end
id=(value) click to toggle source

Sets the primary ID.

# File lib/big_record/model.rb, line 366
def id=(value)
  write_attribute(self.class.primary_key, value)
end
inspect() click to toggle source

Returns the contents of the record as a nicely formatted string.

# File lib/big_record/model.rb, line 428
def inspect
  attributes_as_nice_string = self.class.column_names.collect { |name|
    if has_attribute?(name) || new_record?
      "#{name}: #{attribute_for_inspect(name)}"
    end
  }.compact.join(", ")
  "#<#{self.class} #{attributes_as_nice_string}>"
end
new_record?() click to toggle source

Returns true if this object hasn't been saved yet – that is, a record for the object doesn't yet exist in the data store.

# File lib/big_record/model.rb, line 372
def new_record?
  false
end
preinitialize(attrs = nil) click to toggle source

Callback method meant to be overriden by subclasses if they need to preload some attributes before initializing the record. (usefull when using a meta model where the list of columns depends on the value of an attribute)

# File lib/big_record/model.rb, line 153
def preinitialize(attrs = nil)
  @attributes = {}
end
readonly?() click to toggle source

Records loaded through joins with piggy-back attributes will be marked as read only as they cannot be saved and return true to this query.

# File lib/big_record/model.rb, line 418
def readonly?
  @readonly == true
end
reload(options = nil) click to toggle source

Reloads the attributes of this object from the database. The optional options argument is passed to find when reloading so you may do e.g. record.reload(:lock => true) to reload the same record with an exclusive row lock.

@return Itself with reloaded attributes.

# File lib/big_record/model.rb, line 217
def reload(options = nil)
  @attributes.update(self.class.find(self.id, options).instance_variable_get('@attributes'))
  self
end
respond_to?(method, include_priv = false) click to toggle source

A Person object with a name attribute can ask person.respond_to?(“name”), person.respond_to?(“name=”), and person.respond_to?(“name?”) which will all return true.

Calls superclass method
# File lib/big_record/model.rb, line 333
def respond_to?(method, include_priv = false)
  if @attributes.nil?
    return super
  elsif attr_name = self.class.column_methods_hash[method.to_sym]
    return true if @attributes.include?(attr_name) || attr_name == self.class.primary_key
    return false if self.class.read_methods.include?(attr_name)
  elsif @attributes.include?(method.to_s)
    return true
  elsif md = self.class.match_attribute_method?(method.to_s)
    return true if @attributes.include?(md.pre_match)
  end
  # super must be called at the end of the method, because the inherited respond_to?
  # would return true for generated readers, even if the attribute wasn't present
  super
end
respond_to_without_attributes?(method, include_priv = false)

For checking respond_to? without searching the attributes (which is faster).

Alias for: respond_to?
safe_attributes=(new_attributes, guard_protected_attributes = true) click to toggle source

Safe version of attributes= so that objects can be instantiated even if columns are removed.

@param [Hash] Attribute hash consisting of the column name and their values. @param [true, false] Pass the attributes hash through {#remove_attributes_protected_from_mass_assignment}

# File lib/big_record/model.rb, line 165
def safe_attributes=(new_attributes, guard_protected_attributes = true)
  return if new_attributes.nil?
  attributes = new_attributes.dup
  attributes.stringify_keys!

  multi_parameter_attributes = []
  attributes = remove_attributes_protected_from_mass_assignment(attributes) if guard_protected_attributes

  attributes.each do |k, v|
    begin
      k.include?("(") ? multi_parameter_attributes << [ k, v ] : send(k + "=", v) if v
    rescue
      logger.debug "#{__FILE__}:#{__LINE__} Warning! Ignoring attribute '#{k}' because it doesn't exist anymore"
    end
  end

  begin
    assign_multiparameter_attributes(multi_parameter_attributes)
  rescue
    logger.debug "#{__FILE__}:#{__LINE__} Warning! Ignoring multiparameter attributes because some don't exist anymore"
  end
end
save() click to toggle source
  • No record exists: Creates a new record with values matching those of the object attributes.

  • A record does exist: Updates the record with values matching those of the object attributes.

# File lib/big_record/model.rb, line 378
def save
  raise NotImplemented
end
save!() click to toggle source

Attempts to save the record, but instead of just returning false if it couldn't happen, it raises a RecordNotSaved exception

# File lib/big_record/model.rb, line 384
def save!
  raise NotImplemented
end
to_s() click to toggle source

Default #to_s method that just returns invokes the {#id} method

@return [String] The row identifier/id of the record

# File lib/big_record/model.rb, line 200
def to_s
  id
end
update_attribute(name, value) click to toggle source

Updates a single attribute and saves the record. This is especially useful for boolean flags on existing records. Note: This method is overwritten by the Validation module that'll make sure that updates made with this method doesn't get subjected to validation checks. Hence, attributes can be updated even if the full object isn't valid.

# File lib/big_record/model.rb, line 397
def update_attribute(name, value)
  raise NotImplemented
end
update_attributes(attributes) click to toggle source

Updates all the attributes from the passed-in Hash and saves the record. If the object is invalid, the saving will fail and false will be returned.

# File lib/big_record/model.rb, line 403
def update_attributes(attributes)
  raise NotImplemented
end
update_attributes!(attributes) click to toggle source

Updates an object just like BigRecord::Base#update_attributes but calls save! instead of save so an exception is raised if the record is invalid.

# File lib/big_record/model.rb, line 408
def update_attributes!(attributes)
  raise NotImplemented
end

Protected Instance Methods

assign_multiparameter_attributes(pairs) click to toggle source

Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done by calling new on the column type or aggregation type (through composed_of) object with these parameters. So having the pairs written_on(1) = “2004”, written_on(2) = “6”, written_on(3) = “24”, will instantiate written_on (a date type) with Date.new(“2004”, “6”, “24”). You can also specify a typecast character in the parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum, f for Float, s for String, and a for Array. If all the values for a given attribute are empty, the attribute will be set to nil.

# File lib/big_record/model.rb, line 519
def assign_multiparameter_attributes(pairs)
  execute_callstack_for_multiparameter_attributes(
    extract_callstack_for_multiparameter_attributes(pairs)
  )
end
attribute=(attribute_name, value) click to toggle source

Handle *= for method_missing.

# File lib/big_record/model.rb, line 1106
def attribute=(attribute_name, value)
  write_attribute(attribute_name, value)
end
attribute?(attribute_name) click to toggle source

Handle *? for method_missing.

# File lib/big_record/model.rb, line 1101
def attribute?(attribute_name)
  query_attribute(attribute_name)
end
attribute_before_type_cast(attribute_name) click to toggle source

Handle *_before_type_cast for method_missing.

# File lib/big_record/model.rb, line 1111
def attribute_before_type_cast(attribute_name)
  read_attribute_before_type_cast(attribute_name)
end
attributes_from_column_definition() click to toggle source

Initializes the attributes array with keys matching the columns from the linked table and the values matching the corresponding default value of that column, so that a new instance, or one populated from a passed-in Hash, still has all the attributes that instances loaded from the database would.

# File lib/big_record/model.rb, line 504
def attributes_from_column_definition
  self.class.columns.inject({}) do |attributes, column|
    unless column.name == self.class.primary_key
      attributes[column.name] = column.default
    end
    attributes
  end
end
attributes_protected_by_default() click to toggle source

The primary key and inheritance column can never be set by mass-assignment for security reasons.

# File lib/big_record/model.rb, line 677
def attributes_protected_by_default
  # default = [ self.class.primary_key, self.class.inheritance_column ]
  # default << 'id' unless self.class.primary_key.eql? 'id'
  # default
  []
end
clone_in_persistence_format() click to toggle source
# File lib/big_record/model.rb, line 438
def clone_in_persistence_format
  validate_attributes_schema

  data = {}

  # normalized attributes without the id
  @attributes.keys.each do |key|
    next if !self.class.store_primary_key? and (key == self.class.primary_key)
    value = read_attribute(key)
    if value.kind_of?(Embedded)
      data[key] = value.clone_in_persistence_format
    elsif value.is_a?(Array)
      data[key] = value.collect do |e|
        if e.kind_of?(Embedded)
          e.clone_in_persistence_format
        else
          e
        end
      end
    else
      data[key] = value
    end
  end
  data
end
create() click to toggle source

Creates a record with values matching those of the instance attributes and returns its id. Generate a UUID as the row key.

# File lib/big_record/model.rb, line 588
def create
  raise NotImplemented
end
create_or_update() click to toggle source
# File lib/big_record/model.rb, line 582
def create_or_update
  raise NotImplemented
end
execute_callstack_for_multiparameter_attributes(callstack) click to toggle source

Includes an ugly hack for Time.local instead of Time.new because the latter is reserved by Time itself.

# File lib/big_record/model.rb, line 526
    def execute_callstack_for_multiparameter_attributes(callstack)
      errors = []
      callstack.each do |name, values|
        # TODO: handle aggregation reflections
#        klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
        column = column_for_attribute(name)
        if column
          klass = column.klass

          # Ugly fix for time selectors so that when any value is invalid the value is considered invalid, hence nil
          if values.empty? or (column.type == :time and !values[-2..-1].all?) or ([:date, :datetime].include?(column.type) and !values.all?)
            send(name + "=", nil)
          else
            # End of the ugly time fix...
            values = [2000, 1, 1, values[-2], values[-1]] if column.type == :time and !values[0..2].all?
            begin
              send(name + "=", Time == klass ? (@@default_timezone == :utc ? klass.utc(*values) : klass.local(*values)) : klass.new(*values))
            rescue => ex
              errors << AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name)
            end
          end
        end
      end
      unless errors.empty?
        raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
      end
    end
extract_callstack_for_multiparameter_attributes(pairs) click to toggle source
# File lib/big_record/model.rb, line 554
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)

    position = find_parameter_position(multiparameter_name)
    value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
    attributes[attribute_name] << [position, value]
  end
  attributes.each { |name, values| attributes[name] = values.sort_by{ |v| v.first }.collect { |v| v.last } }
end
find_parameter_position(multiparameter_name) click to toggle source
# File lib/big_record/model.rb, line 573
def find_parameter_position(multiparameter_name)
  multiparameter_name.scan(/\(([0-9]*).*\)/).first.first
end
generate_new_id() click to toggle source

Generate a new id with the UUIDTools library. Override this method to use another id generator.

# File lib/big_record/model.rb, line 496
def generate_new_id
  UUIDTools::UUID.random_create.to_s
end
log_protected_attribute_removal(*attributes) click to toggle source
# File lib/big_record/model.rb, line 672
def log_protected_attribute_removal(*attributes)
  logger.debug "WARNING: Can't mass-assign these protected attributes: #{attributes.join(', ')}"
end
method_missing(method_id, *args, &block) click to toggle source

Allows access to the object attributes, which are held in the @attributes hash, as were they first-class methods. So a Person class with a name attribute can use Person#name and Person#name= and never directly use the attributes hash – except for multiple assigns with ActiveRecord#attributes=. A Milestone class can also ask Milestone#completed? to test that the completed attribute is not nil or 0.

It's also possible to instantiate related objects, so a Client class belonging to the clients table with a master_id foreign key can instantiate master through Client#master.

Calls superclass method
# File lib/big_record/model.rb, line 613
def method_missing(method_id, *args, &block)
  method_name = method_id.to_s
  if column_for_attribute(method_name) or
      ((md = /\?$/.match(method_name)) and
      column_for_attribute(query_method_name = md.pre_match) and
      method_name = query_method_name)
    define_read_methods if self.class.read_methods.empty? && self.class.generate_read_methods
    md ? query_attribute(method_name) : read_attribute(method_name)
  elsif self.class.primary_key.to_s == method_name
    id
  elsif (md = self.class.match_attribute_method?(method_name))
    attribute_name, method_type = md.pre_match, md.to_s
    if column_for_attribute(attribute_name)
      __send__("attribute#{method_type}", attribute_name, *args, &block)
    else
      super
    end
  else
    super
  end
end
quote_value(value, column = nil) click to toggle source

Quote strings appropriately for SQL statements.

# File lib/big_record/model.rb, line 578
def quote_value(value, column = nil)
  self.class.connection.quote(value, column)
end
read_attribute(attr_name) click to toggle source

Returns the value of the attribute identified by attr_name after it has been typecast (for example, “2004-12-12” in a data column is cast to a date object, like Date.new(2004, 12, 12)).

# File lib/big_record/model.rb, line 687
def read_attribute(attr_name)
  attr_name = attr_name.to_s
  if !(value = @attributes[attr_name]).nil?
    if column = column_for_attribute(attr_name)
      write_attribute(attr_name, column.type_cast(value))
    else
      value
    end
  else
    nil
  end
end
read_attribute_before_type_cast(attr_name) click to toggle source
# File lib/big_record/model.rb, line 700
def read_attribute_before_type_cast(attr_name)
  @attributes[attr_name.to_s]
end
remove_attributes_protected_from_mass_assignment(attributes) click to toggle source

Removes any attributes from the argument hash that have been declared as protected from mass assignment. See the {#attr_protected} and {#attr_accessible} macros to define these attributes.

# File lib/big_record/model.rb, line 638
def remove_attributes_protected_from_mass_assignment(attributes)
  safe_attributes =
    if self.class.accessible_attributes.nil? && self.class.protected_attributes.nil?
      attributes.reject { |key, value| attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
    elsif self.class.protected_attributes.nil?
      attributes.reject { |key, value| !self.class.accessible_attributes.include?(key.gsub(/\(.+/, "")) || attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
    elsif self.class.accessible_attributes.nil?
      attributes.reject { |key, value| self.class.protected_attributes.include?(key.gsub(/\(.+/,"")) || attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
    else
      raise "Declare either attr_protected or attr_accessible for #{self.class}, but not both."
    end

  if !self.new_record? && !self.class.create_accessible_attributes.nil?
    safe_attributes = safe_attributes.delete_if{ |key, value| self.class.create_accessible_attributes.include?(key.gsub(/\(.+/,"")) }
  end

  removed_attributes = attributes.keys - safe_attributes.keys

  if removed_attributes.any?
    log_protected_attribute_removal(removed_attributes)
  end

  safe_attributes
end
remove_readonly_attributes(attributes) click to toggle source

Removes attributes which have been marked as readonly.

# File lib/big_record/model.rb, line 664
def remove_readonly_attributes(attributes)
  unless self.class.readonly_attributes.nil?
    attributes.delete_if { |key, value| self.class.readonly_attributes.include?(key.gsub(/\(.+/,"")) }
  else
    attributes
  end
end
type_cast_attribute_value(multiparameter_name, value) click to toggle source
# File lib/big_record/model.rb, line 569
def type_cast_attribute_value(multiparameter_name, value)
  multiparameter_name =~ /\([0-9]*([a-z])\)/ ? value.send("to_" + $1) : value
end
update() click to toggle source

Updates the associated record with values matching those of the instance attributes. Returns the number of affected rows.

# File lib/big_record/model.rb, line 594
def update
  raise NotImplemented
end
update_bigrecord() click to toggle source

Update this record in hbase. Cannot be directly in the method 'update' because it would trigger callbacks and therefore weird behaviors.

# File lib/big_record/model.rb, line 600
def update_bigrecord
  timestamp = self.respond_to?(:updated_at) ? self.updated_at.to_bigrecord_timestamp : Time.now.to_bigrecord_timestamp
  connection.update(self.class.table_name, id, clone_in_persistence_format, timestamp)
end
validate_attribute_type(value, column) click to toggle source
# File lib/big_record/model.rb, line 488
def validate_attribute_type(value, column)
  unless (value == nil) or value.kind_of?(column.klass)
    raise WrongAttributeDataType, "#{human_attribute_name(column.name)} has the wrong type. Expected #{column.klass}. Record is #{value.class}"
  end
end
validate_attributes_schema() click to toggle source

Validate the type of the values in the attributes hash

# File lib/big_record/model.rb, line 465
def validate_attributes_schema
  @attributes.keys.each do |key|
    value = read_attribute(key)
    next unless value
    # type validation
    if (column = column_for_attribute(key)) and !key.ends_with?(":")
      if column.collection?
        unless value.is_a?(Array)
          raise WrongAttributeDataType, "#{human_attribute_name(column.name)} has the wrong type. Expected collection of #{column.klass}. Record is #{value.class}"
        end
        value.each do |v|
          validate_attribute_type(v, column)
        end
      else
        validate_attribute_type(value, column)
      end
    else
      # Don't save attributes set in a previous schema version
      @attributes.delete(key)
    end
  end
end

Private Instance Methods

convert_number_column_value(value) click to toggle source
# File lib/big_record/model.rb, line 771
def convert_number_column_value(value)
  case value
    when FalseClass then 0
    when TrueClass then 1
    when '' then nil
    else value
  end
end
define_question_method(attr_name) click to toggle source

Define an attribute ? method.

# File lib/big_record/model.rb, line 734
def define_question_method(attr_name)
  unless attr_name.to_s == self.class.primary_key.to_s
    self.class.read_methods << "#{attr_name}?"
  end

  evaluate_read_method attr_name, "def #{attr_name}?; query_attribute('#{attr_name}'); end"
end
define_read_method(symbol, attr_name, column) click to toggle source

Define an attribute reader method. Cope with a nil column.

# File lib/big_record/model.rb, line 721
def define_read_method(symbol, attr_name, column)
  cast_code = column.type_cast_code('v') if column
  access_code = cast_code ? "(v=@attributes['#{attr_name}']) && #{cast_code}" : "@attributes['#{attr_name}']"

  unless attr_name.to_s == self.class.primary_key.to_s
    access_code = access_code.insert(0, "raise NoMethodError, 'missing attribute: #{attr_name}', caller unless @attributes.has_key?('#{attr_name}'); ")
    self.class.read_methods << attr_name
  end

  evaluate_read_method attr_name, "def #{symbol}; #{access_code}; end"
end
define_read_methods() click to toggle source

Called on first read access to any given column and generates reader methods for all columns in the ::columns_hash if BigRecord::Base.generate_read_methods is set to true.

# File lib/big_record/model.rb, line 708
def define_read_methods
  self.class.columns_hash.each do |name, column|
    unless respond_to_without_attributes?(name)
      define_read_method(name.to_sym, name, column)
    end

    unless respond_to_without_attributes?("#{name}?")
      define_question_method(name)
    end
  end
end
evaluate_read_method(attr_name, method_definition) click to toggle source

Evaluate the definition for an attribute reader or ? method

# File lib/big_record/model.rb, line 743
def evaluate_read_method(attr_name, method_definition)
  begin
    self.class.class_eval(method_definition)
  rescue SyntaxError => err
    self.class.read_methods.delete(attr_name)
    if logger
      logger.warn "Exception occurred during reader method compilation."
      logger.warn "Maybe #{attr_name} is not a valid Ruby identifier?"
      logger.warn "#{err.message}"
    end
  end
end
write_attribute(attr_name, value) click to toggle source

Updates the attribute identified by attr_name with the specified value. Empty strings for fixnum and float columns are turned into nil.

# File lib/big_record/model.rb, line 758
def write_attribute(attr_name, value)
  attr_name = attr_name.to_s
  column = column_for_attribute(attr_name)

  raise "Invalid column for this bigrecord object (e.g., you tried to set a predicate value for an entity that is out of the predicate scope)" if column == nil

  if column.number?
    @attributes[attr_name] = convert_number_column_value(value)
  else
    @attributes[attr_name] = value
  end
end