Class: FlexColumns::Contents::FlexColumnContentsBase
- Inherits:
-
Object
- Object
- FlexColumns::Contents::FlexColumnContentsBase
- Extended by:
- Definition::FlexColumnContentsClass
- Includes:
- ActiveModel::Validations
- Defined in:
- lib/flex_columns/contents/flex_column_contents_base.rb
Overview
When you declare a flex column, we actually generate a brand-new Class for that column; instances of that flex column are instances of this new Class. This class acquires functionality from two places: FlexColumnContentsBase, which defines its instance methods, and FlexColumnContentsClass, which defines its class methods. (While FlexColumnContentsBase is an actual Class, FlexColumnContentsClass is a Module that FlexColumnContentsBase extends. Both could be combined, but, simply for readability and maintainability, it was better to make them separate.)
This Class therefore defines the methods that are available on an instance of a flex-column class – on the object returned by my_user.user_attributes, for example.
Constant Summary
Constants included from Definition::FlexColumnContentsClass
Definition::FlexColumnContentsClass::DEFAULT_MAX_JSON_LENGTH_BEFORE_COMPRESSION
Instance Attribute Summary
Attributes included from Definition::FlexColumnContentsClass
Instance Method Summary collapse
-
#[](field_name) ⇒ Object
Provides Hash-style read access to fields in the flex column.
-
#[]=(field_name, new_value) ⇒ Object
Provides Hash-style write access to fields in the flex column.
-
#before_save! ⇒ Object
Called via the ActiveRecord::Base#before_save hook that gets installed on the enclosing model instance.
-
#before_validation! ⇒ Object
Called via the ActiveRecord::Base#before_validation hook that gets installed on the enclosing model instance.
-
#describe_flex_column_data_source ⇒ Object
Returns a String, appropriate for human consumption, that describes the model instance we’re created from (or raw String, if that’s the case).
-
#initialize(input) ⇒ FlexColumnContentsBase
constructor
Creates a new instance.
-
#keys ⇒ Object
Returns an Array containing the names (as Symbols) of all fields on this flex-column object that currently have any data set for them — i.e., that are not
nil. -
#notification_hash_for_flex_column_data_source ⇒ Object
Returns a Hash, appropriate for integration into the payload of an ActiveSupport::Notification call, that describes the model instance we’re created from (or raw String, if that’s the case).
-
#to_json ⇒ Object
Returns a JSON string representing the current contents of this flex column.
-
#to_model ⇒ Object
This is required by ActiveModel::Validations; it’s asking, “what’s the ActiveModel object I should use for validation purposes?”.
-
#to_stored_data ⇒ Object
Returns a String representing exactly the data that will get stored in the database, for this flex column.
-
#touch! ⇒ Object
A flex column has been “touched” if it has had at least one field changed to a different value than it had before, or if someone has called #touch! on it.
-
#touched? ⇒ Boolean
Has at least one field in the column been changed, or has someone called #touch! ?.
Methods included from Definition::FlexColumnContentsClass
_flex_columns_create_column_data, all_field_names, delegation_prefix, delegation_type, field, field_named, field_with_json_storage_name, fields_are_private_by_default?, include_fields_into, is_flex_column_class?, object_for, requires_serialization_on_save?, setup!, sync_methods!
Constructor Details
#initialize(input) ⇒ FlexColumnContentsBase
Creates a new instance. input is the source of data we should use: normally this is an instance of the enclosing model class (e.g., User), but it can also be a simple String (if you’re creating an instance using the bulk API – HasFlexColumns#create_flex_objects_from, for example) containing the stored JSON for this object, or nil (if you’re doing the same, but have no source data).
The reason this class hangs onto the whole model instance, instead of just the string, is twofold:
-
It needs to be able to add validation errors back onto the model instance;
-
It wants to be able to pass a description of the model instance into generated exceptions and the ActiveSupport::Notifications calls made, so that when things go wrong or you’re doing performance work, you can understand what row in what table contains incorrect data or data that is making things slow.
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/flex_columns/contents/flex_column_contents_base.rb', line 38 def initialize(input) storage_string = nil if input.kind_of?(String) @model_instance = nil storage_string = input @source_string = input elsif (! input) @model_instance = nil storage_string = nil elsif input.class.equal?(self.class.model_class) @model_instance = input storage_string = @model_instance[self.class.column_name] else raise ArgumentError, %{You can create a #{self.class.name} from a String, nil, or #{self.class.model_class.name} (#{self.class.model_class.object_id}), not #{input.inspect} (#{input.object_id}).} end # Creates an instance of FlexColumns::Contents::ColumnData, which is the thing that does most of the actual # work with the underlying data for us. @column_data = self.class._flex_columns_create_column_data(storage_string, self) end |
Instance Method Details
#[](field_name) ⇒ Object
Provides Hash-style read access to fields in the flex column. This delegates to the ColumnData object, which does most of the actual work.
101 102 103 |
# File 'lib/flex_columns/contents/flex_column_contents_base.rb', line 101 def [](field_name) column_data[field_name] end |
#[]=(field_name, new_value) ⇒ Object
Provides Hash-style write access to fields in the flex column. This delegates to the ColumnData object, which does most of the actual work.
107 108 109 |
# File 'lib/flex_columns/contents/flex_column_contents_base.rb', line 107 def []=(field_name, new_value) column_data[field_name] = new_value end |
#before_save! ⇒ Object
Called via the ActiveRecord::Base#before_save hook that gets installed on the enclosing model instance. This is what actually serializes the column data and sets it on the ActiveRecord model when it’s being saved.
159 160 161 162 163 164 165 166 |
# File 'lib/flex_columns/contents/flex_column_contents_base.rb', line 159 def before_save! return unless model_instance # Make sure we only save if we need to -- otherwise, save the CPU cycles. if self.class.requires_serialization_on_save?(model_instance) model_instance[column_name] = column_data.to_stored_data end end |
#before_validation! ⇒ Object
Called via the ActiveRecord::Base#before_validation hook that gets installed on the enclosing model instance. This runs any validations that are present on this flex-column object, and then propagates any errors back to the enclosing model instance, so that errors show up there, as well.
133 134 135 136 137 138 139 140 |
# File 'lib/flex_columns/contents/flex_column_contents_base.rb', line 133 def before_validation! return unless model_instance unless valid? errors.each do |name, | model_instance.errors.add("#{column_name}.#{name}", ) end end end |
#describe_flex_column_data_source ⇒ Object
Returns a String, appropriate for human consumption, that describes the model instance we’re created from (or raw String, if that’s the case). This is used solely by the errors in FlexColumns::Errors, and is used to give good, actionable diagnostic messages when something goes wrong.
64 65 66 67 68 69 70 71 72 |
# File 'lib/flex_columns/contents/flex_column_contents_base.rb', line 64 def describe_flex_column_data_source if model_instance out = self.class.model_class.name.dup out << " ID #{model_instance.id}" if model_instance.id out << ", column #{self.class.column_name.inspect}" else "(data passed in by client, for #{self.class.model_class.name}, column #{self.class.column_name.inspect})" end end |
#keys ⇒ Object
Returns an Array containing the names (as Symbols) of all fields on this flex-column object that currently have any data set for them — i.e., that are not nil.
170 171 172 |
# File 'lib/flex_columns/contents/flex_column_contents_base.rb', line 170 def keys column_data.keys end |
#notification_hash_for_flex_column_data_source ⇒ Object
Returns a Hash, appropriate for integration into the payload of an ActiveSupport::Notification call, that describes the model instance we’re created from (or raw String, if that’s the case). This is used by the calls made to ActiveSupport::Notifications when a flex-column object is serialized or deserialized, and is used to give good, actionable content when monitoring system performance.
78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/flex_columns/contents/flex_column_contents_base.rb', line 78 def notification_hash_for_flex_column_data_source out = { :model_class => self.class.model_class, :column_name => self.class.column_name } if model_instance out[:model] = model_instance else out[:source] = @source_string end out end |
#to_json ⇒ Object
Returns a JSON string representing the current contents of this flex column. Note that this is not always exactly what gets stored in the database, because of binary columns and compression; for that, use #to_stored_data, below.
145 146 147 |
# File 'lib/flex_columns/contents/flex_column_contents_base.rb', line 145 def to_json column_data.to_json end |
#to_model ⇒ Object
This is required by ActiveModel::Validations; it’s asking, “what’s the ActiveModel object I should use for validation purposes?”. And, here, it’s this same object.
95 96 97 |
# File 'lib/flex_columns/contents/flex_column_contents_base.rb', line 95 def to_model self end |
#to_stored_data ⇒ Object
Returns a String representing exactly the data that will get stored in the database, for this flex column. This will be a UTF-8-encoded String containing pure JSON if this is a textual column, or, if it’s a binary column, either a UTF-8-encoded JSON String prefixed by a small header, or a BINARY-encoded String containing GZip’ed JSON, prefixed by a small header.
153 154 155 |
# File 'lib/flex_columns/contents/flex_column_contents_base.rb', line 153 def to_stored_data column_data.to_stored_data end |
#touch! ⇒ Object
A flex column has been “touched” if it has had at least one field changed to a different value than it had before, or if someone has called #touch! on it. If a column has not been touched, validations are not run on it, nor is it re-serialized back out to the database on save!. Generally, this is a good thing: it increases performance substantially for times when you haven’t actually changed the flex column’s contents at all. It does mean that invalid data won’t be detected and unknown fields won’t be removed (if you’ve specified :unknown_fields => delete), however.
There may be times, however, when you want to make sure the column is stored back out (including removing any unknown fields, if you selected that option), or to make sure that validations get run, no matter what. In this case, you can call #touch!.
121 122 123 |
# File 'lib/flex_columns/contents/flex_column_contents_base.rb', line 121 def touch! column_data.touch! end |
#touched? ⇒ Boolean
Has at least one field in the column been changed, or has someone called #touch! ?
126 127 128 |
# File 'lib/flex_columns/contents/flex_column_contents_base.rb', line 126 def touched? column_data.touched? end |