module FlexColumns::HasFlexColumns

HasFlexColumns is the module that gets included in an ActiveRecord model class as soon as it declares a flex column (using FlexColumns::ActiveRecord::Base#flex_column). While most of the actual work of maintaining and working with a flex column is accomplished by the FlexColumns::Definition::FlexColumnContentsClass module and the FlexColumns::Contents::FlexColumnContentsBase class, there remains, nevertheless, some important work to do here.

Public Instance Methods

_flex_column_owned_object_for(column_name, create_if_needed = true) click to toggle source

Returns the correct flex-column object for the given column name. This simply creates an instance of the appropriate flex-column class, and saves it away so it will be returned again if someone requests the object for the same column later.

This method almost never gets invoked directly; instead, everything calls it via FlexColumns::ActiveRecord::Base#_flex_column_object_for.

# File lib/flex_columns/has_flex_columns.rb, line 55
def _flex_column_owned_object_for(column_name, create_if_needed = true)
  column_name = self.class._flex_column_normalize_name(column_name)

  out = _flex_column_objects[column_name]
  if (! out) && create_if_needed
    out = _flex_column_objects[column_name] = self.class._flex_column_class_for(column_name).new(self)
  end
  out
end
_flex_columns_before_save!() click to toggle source

Before we save this model, make sure each flex column has a chance to serialize itself up and assign itself properly to this model object. Note that we only need to call through to flex-column objects that have actually been instantiated, since, by definition, there’s no way the contents of any other flex columns could possibly have been changed.

# File lib/flex_columns/has_flex_columns.rb, line 30
def _flex_columns_before_save!
  self.class._all_flex_column_names.each do |flex_column_name|
    klass = self.class._flex_column_class_for(flex_column_name)
    if klass.requires_serialization_on_save?(self)
      _flex_column_object_for(flex_column_name).before_save!
    end
  end
end
_flex_columns_before_validation!() click to toggle source

Before we validate this model, make sure each flex column has a chance to run its validations and propagate any errors back to this model. Note that we need to call through to any flex-column object that has a validation defined, since we want to comply with Rails’ validation strategy: validations run whenever you save an object, whether you’ve changed that particular attribute or not.

# File lib/flex_columns/has_flex_columns.rb, line 43
def _flex_columns_before_validation!
  _all_present_flex_column_objects.each do |flex_column_object|
    flex_column_object.before_validation!
  end
end
as_json(options = { }) click to toggle source

See above. We’re actually overriding both methods, because read_attribute_for_serialization doesn’t exist in ActiveRecord 3.0.x.

Calls superclass method
# File lib/flex_columns/has_flex_columns.rb, line 82
def as_json(options = { })
  flex_column_names = self.class._all_flex_column_names
  out = super(:except => flex_column_names)

  flex_columns_hash = { }
  flex_column_names.each do |column_name|
    hash = _flex_column_object_for(column_name).to_hash_for_serialization
    flex_columns_hash[column_name] = hash unless hash.empty?
  end

  if include_root_in_json && out.keys.length == 1
    out[out.keys.first].merge!(flex_columns_hash)
  else
    out.merge!(flex_columns_hash)
  end

  out
end
attribute_for_inspect(attr_name) click to toggle source

This little-know ActiveRecord method gets called to produce a string for inspect for a particular attribute. Because the default implementation uses read_attribute, if we don’t override it, it will simply return our actual string in the database; if this is compressed data, this is meaningless to a programmer. So we override this to instead deserialize the column and call inspect on the actual FlexColumnContentsBase object, which shows interesting information.

NOTE: See the warning comment above FlexColumnContentsBase#inspect, which points out that this will deserialize the column if it hasn’t already – so calling this has a performance penalty. This should be fine, since calling inspect in bulk isn’t something a program should be doing in production mode anyway, but it’s worth noting.

Calls superclass method
# File lib/flex_columns/has_flex_columns.rb, line 118
def attribute_for_inspect(attr_name)
  cn =  self.class._all_flex_column_names
  if cn.include?(attr_name.to_sym)
    _flex_column_object_for(attr_name).inspect
  else
    super(attr_name)
  end
end
read_attribute_for_serialization(attribute_name) click to toggle source

When ActiveRecord serializes an entire ActiveRecord object (for example, if you call to_json on it), it reads each column individually using this method – which, in the default implementation, just calls send. (Well, it’s actually aliased to send, but it has the same effect.)

However, if you’re serializing an ActiveRecord model that contains a flex column, you almost certainly just want that to behave as if the flex_column is a Hash, and serialize it that way. So we override it to do just that right here.

Calls superclass method
# File lib/flex_columns/has_flex_columns.rb, line 72
def read_attribute_for_serialization(attribute_name)
  if self.class._has_flex_column_named?(attribute_name)
    _flex_column_object_for(attribute_name).to_hash_for_serialization
  else
    super(attribute_name)
  end
end
reload(*args) click to toggle source

When you reload a model object, we should reload its flex-column objects, too.

Calls superclass method
# File lib/flex_columns/has_flex_columns.rb, line 102
def reload(*args)
  out = super(*args)
  @_flex_column_objects = { }
  out
end

Private Instance Methods

_all_present_flex_column_objects() click to toggle source

Returns all flex-column objects that have been instantiated – that is, any flex-column object that anybody has asked for yet.

# File lib/flex_columns/has_flex_columns.rb, line 135
def _all_present_flex_column_objects
  _flex_column_objects.values
end
_flex_column_objects() click to toggle source

Returns the Hash that we keep flex-column objects in, indexed by column name.

# File lib/flex_columns/has_flex_columns.rb, line 129
def _flex_column_objects
  @_flex_column_objects ||= { }
end