Module: FlexColumns::HasFlexColumns
- Extended by:
- ActiveSupport::Concern
- Defined in:
- lib/flex_columns/has_flex_columns.rb
Overview
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.
Defined Under Namespace
Modules: ClassMethods
Instance Method Summary collapse
-
#_flex_column_owned_object_for(column_name, create_if_needed = true) ⇒ Object
Returns the correct flex-column object for the given column name.
-
#_flex_columns_before_save! ⇒ Object
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.
-
#_flex_columns_before_validation! ⇒ Object
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.
-
#as_json(options = { }) ⇒ Object
See above.
-
#attribute_for_inspect(attr_name) ⇒ Object
This little-know ActiveRecord method gets called to produce a string for #inspect for a particular attribute.
-
#read_attribute_for_serialization(attribute_name) ⇒ Object
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.
-
#reload(*args) ⇒ Object
When you reload a model object, we should reload its flex-column objects, too.
Instance Method Details
#_flex_column_owned_object_for(column_name, create_if_needed = true) ⇒ Object
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.
55 56 57 58 59 60 61 62 63 |
# 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! ⇒ Object
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.
30 31 32 33 34 35 36 37 |
# 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! ⇒ Object
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.
43 44 45 46 47 |
# 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 = { }) ⇒ Object
See above. We’re actually overriding both methods, because #read_attribute_for_serialization doesn’t exist in ActiveRecord 3.0.x.
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/flex_columns/has_flex_columns.rb', line 82 def as_json( = { }) 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) ⇒ Object
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.
118 119 120 121 122 123 124 125 |
# 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) ⇒ Object
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.
72 73 74 75 76 77 78 |
# 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) ⇒ Object
When you reload a model object, we should reload its flex-column objects, too.
102 103 104 105 106 |
# File 'lib/flex_columns/has_flex_columns.rb', line 102 def reload(*args) out = super(*args) @_flex_column_objects = { } out end |