Class: FlexColumns::Definition::FieldDefinition
- Inherits:
-
Object
- Object
- FlexColumns::Definition::FieldDefinition
- Defined in:
- lib/flex_columns/definition/field_definition.rb
Overview
A FieldDefinition represents, well, the definition of a field. One of these objects is created for each field you declare in a flex column. It keeps track of (at minimum) the name of the field; it also is responsible for implementing our “shorthand types” system (where declaring your field as :integer
adds a validation that requires it to be an integer, for example).
Perhaps most significantly, a FieldDefinition object is responsible for creating the appropriate methods on the flex-column class and on the model class, and also for adding methods to classes that have invoked IncludeFlexColumns#include_flex_columns_from.
Instance Attribute Summary collapse
-
#field_name ⇒ Object
readonly
Returns the value of attribute field_name.
Class Method Summary collapse
-
.normalize_name(name) ⇒ Object
Given the name of a field, returns a normalized version of that name – so we can compare using == without worrying about String vs.
Instance Method Summary collapse
-
#add_methods_to_flex_column_class!(dynamic_methods_module) ⇒ Object
Defines appropriate accessor methods for this field on the given DynamicMethodsModule, which should be included in the flex-column class (not the model class).
-
#add_methods_to_included_class!(dynamic_methods_module, association_name, target_class, options) ⇒ Object
Defines appropriate accessor methods for this field on the given DynamicMethodsModule, which should be included in some target model class that has said
include_flex_columns_from
on the clsas containing this field. -
#add_methods_to_model_class!(dynamic_methods_module, model_class) ⇒ Object
Defines appropriate accessor methods for this field on the given DynamicMethodsModule, which should be included in the model class.
-
#initialize(flex_column_class, field_name, additional_arguments, options) ⇒ FieldDefinition
constructor
Creates a new instance.
-
#json_storage_name ⇒ Object
Returns the key under which the field’s value should be stored in the JSON.
Constructor Details
#initialize(flex_column_class, field_name, additional_arguments, options) ⇒ FieldDefinition
Creates a new instance. flex_column_class
is the Class we created for this flex column – i.e., a class that inherits from FlexColumns::Contents::FlexColumnContentsBase. field_name
is the name of the field. additional_arguments
is an Array containing any additional arguments that were passed – right now, that can only be the type of the field (e.g., :integer
, etc.). options
is any options that were passed; this can contain:
:visibility, :null, :enum, :limit, :json
- :visibility
-
Can be set to
:public
or:private
; will override the default visibility for fields specified on the flex-column class itself. - :null
-
If present and set to
false
, a validation requiring data in this field will be added. - :enum
-
If present, must be mapped to an Array; a validation requiring the data to be one of the elements of the array will be added.
- :limit
-
If present, must be mapped to an integer; a validation requiring the length of the data to be at most this value will be added.
- :json
-
If present, must be mapped to a String or Symbol; this specifies that the field should be stored under the given key in the JSON, rather than its field name.
44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/flex_columns/definition/field_definition.rb', line 44 def initialize(flex_column_class, field_name, additional_arguments, ) unless flex_column_class.respond_to?(:is_flex_column_class?) && flex_column_class.is_flex_column_class? raise ArgumentError, "You can't define a flex-column field against #{flex_column_class.inspect}; that isn't a flex-column class." end () @flex_column_class = flex_column_class @field_name = self.class.normalize_name(field_name) @options = @field_type = nil apply_additional_arguments(additional_arguments) apply_validations! end |
Instance Attribute Details
#field_name ⇒ Object (readonly)
Returns the value of attribute field_name.
26 27 28 |
# File 'lib/flex_columns/definition/field_definition.rb', line 26 def field_name @field_name end |
Class Method Details
.normalize_name(name) ⇒ Object
Given the name of a field, returns a normalized version of that name – so we can compare using == without worrying about String vs. Symbol and so on.
15 16 17 18 19 20 21 22 23 |
# File 'lib/flex_columns/definition/field_definition.rb', line 15 def normalize_name(name) case name when Symbol then name when String then raise "You must supply a non-empty String, not: #{name.inspect}" if name.strip.length == 0 name.strip.downcase.to_sym else raise ArgumentError, "You must supply a name, not: #{name.inspect}" end end |
Instance Method Details
#add_methods_to_flex_column_class!(dynamic_methods_module) ⇒ Object
Defines appropriate accessor methods for this field on the given DynamicMethodsModule, which should be included in the flex-column class (not the model class). These are quite simple; they always exist (and should overwrite any existing methods, since we’re last-definition-wins). We just need to make them work, and make them private, if needed.
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/flex_columns/definition/field_definition.rb', line 68 def add_methods_to_flex_column_class!(dynamic_methods_module) fn = field_name dynamic_methods_module.define_method(fn) do self[fn] end dynamic_methods_module.define_method("#{fn}=") do |x| self[fn] = x end if private? dynamic_methods_module.private(fn) dynamic_methods_module.private("#{fn}=") end end |
#add_methods_to_included_class!(dynamic_methods_module, association_name, target_class, options) ⇒ Object
Defines appropriate accessor methods for this field on the given DynamicMethodsModule, which should be included in some target model class that has said include_flex_columns_from
on the clsas containing this field. association_name
is the name of the association method name that, when called on the class that includes the DynamicMethodsModule, will return an instance of the model class in which this field lives. target_class
is the target class we’re defining methods on, so that we can check if we’re going to conflict with some method there that we should not clobber.
options
can contain:
- :visibility
-
If
:private
, then methods will be defined as private. - :prefix
-
If specified, then methods will be prefixed with the given prefix. This will override the prefix specified on the flex-column class, if any.
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/flex_columns/definition/field_definition.rb', line 129 def add_methods_to_included_class!(dynamic_methods_module, association_name, target_class, ) return if (! flex_column_class.delegation_type) prefix = if .has_key?(:prefix) then [:prefix] else flex_column_class.delegation_prefix end is_private = private? || (flex_column_class.delegation_type == :private) || ([:visibility] == :private) if is_private && [:visibility] == :public raise ArgumentError, %{You asked for public visibility for methods included from association #{association_name.inspect}, but the flex column #{flex_column_class.model_class.name}.#{flex_column_class.column_name} has its methods defined with private visibility (either in the flex column itself, or at the model level). You can't have methods be 'more public' in the included class than they are in the class they're being included from.} end mn = field_name mn = "#{prefix}_#{mn}".to_sym if prefix fcc = flex_column_class fn = field_name if target_class._flex_columns_safe_to_define_method?(mn) dynamic_methods_module.define_method(mn) do associated_object = send(association_name) || send("build_#{association_name}") flex_column_object = associated_object.send(fcc.column_name) flex_column_object.send(fn) end dynamic_methods_module.define_method("#{mn}=") do |x| associated_object = send(association_name) || send("build_#{association_name}") flex_column_object = associated_object.send(fcc.column_name) flex_column_object.send("#{fn}=", x) end if is_private dynamic_methods_module.private(mn) dynamic_methods_module.private("#{mn}=") end end end |
#add_methods_to_model_class!(dynamic_methods_module, model_class) ⇒ Object
Defines appropriate accessor methods for this field on the given DynamicMethodsModule, which should be included in the model class. We also pass model_class
so that we can check to see if we’re going to conflict with one of its columns first, or other methods we shouldn’t clobber.
We need to respect visibility (public or private) of methods, and the delegation prefix assigned at the flex-column level.
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/flex_columns/definition/field_definition.rb', line 91 def add_methods_to_model_class!(dynamic_methods_module, model_class) return if (! flex_column_class.delegation_type) # :delegate => false on the flex column means don't delegate from the model at all mn = field_name mn = "#{flex_column_class.delegation_prefix}_#{mn}".to_sym if flex_column_class.delegation_prefix if model_class._flex_columns_safe_to_define_method?(mn) fcc = flex_column_class fn = field_name should_be_private = (private? || flex_column_class.delegation_type == :private) dynamic_methods_module.define_method(mn) do flex_instance = fcc.object_for(self) flex_instance[fn] end dynamic_methods_module.private(mn) if should_be_private dynamic_methods_module.define_method("#{mn}=") do |x| flex_instance = fcc.object_for(self) flex_instance[fn] = x end dynamic_methods_module.private("#{mn}=") if should_be_private end end |
#json_storage_name ⇒ Object
Returns the key under which the field’s value should be stored in the JSON.
60 61 62 |
# File 'lib/flex_columns/definition/field_definition.rb', line 60 def json_storage_name ([:json] || field_name).to_s.strip.downcase.to_sym end |