class ActiveGraph::Shared::DeclaredProperties
The DeclaredPropertyuManager holds details about objects created as a result of calling the property class method on a class that includes ActiveGraph::Node
or ActiveGraph::Relationship
. There are many options that are referenced frequently, particularly during load and save, so this provides easy access and a way of separating behavior from the general Active{obj} modules.
See ActiveGraph::Shared::DeclaredProperty
for definitions of the property objects themselves.
Constants
- EXCLUDED_TYPES
Attributes
Public Class Methods
Each class that includes ActiveGraph::Node
or ActiveGraph::Relationship
gets one instance of this class. @param [#declared_properties] klass An object that has the declared_properties method.
# File lib/active_graph/shared/declared_properties.rb 16 def initialize(klass) 17 @klass = klass 18 end
Public Instance Methods
# File lib/active_graph/shared/declared_properties.rb 20 def [](key) 21 registered_properties[key.to_sym] 22 end
During object wrap, a hash is needed that contains each declared property with a nil value. The active_attr dependency is capable of providing this but it is expensive and calculated on the fly each time it is called. Rather than rely on that, we build this progressively as properties are registered. When the node or rel is loaded, this is used as a template.
# File lib/active_graph/shared/declared_properties.rb 69 def attributes_nil_hash 70 @_attributes_nil_hash ||= {}.tap do |attr_hash| 71 registered_properties.each_pair do |k, prop_obj| 72 val = prop_obj.default_value 73 attr_hash[k.to_s] = val 74 end 75 end.freeze 76 end
During object wrapping, a props hash is built with string keys but ActiveGraph::Core
provides symbols. Rather than a `to_s` or `symbolize_keys` during every load, we build a map of symbol-to-string to speed up the process. This increases memory used by the gem but reduces object allocation and GC, so it is faster in practice.
# File lib/active_graph/shared/declared_properties.rb 82 def attributes_string_map 83 @_attributes_string_map ||= {}.tap do |attr_hash| 84 attributes_nil_hash.each_key { |k| attr_hash[k.to_sym] = k } 85 end.freeze 86 end
# File lib/active_graph/shared/declared_properties.rb 45 def constraint_or_fail!(key, id_property_name, type = :unique) 46 return if key == id_property_name 47 fail "Cannot constraint undeclared property #{property}" unless property?(key) 48 registered_properties[key].constraint!(type) 49 end
The :default option in ActiveGraph::Node#property class method allows for setting a default value instead of nil on declared properties. This holds those values.
# File lib/active_graph/shared/declared_properties.rb 53 def declared_property_defaults 54 @_default_property_values ||= {} 55 end
# File lib/active_graph/shared/declared_properties.rb 39 def index_or_fail!(key, id_property_name, type = :exact) 40 return if key == id_property_name 41 fail "Cannot index undeclared property #{key}" unless property?(key) 42 registered_properties[key].index!(type) 43 end
# File lib/active_graph/shared/declared_properties.rb 61 def indexed_properties 62 registered_properties.select { |_, p| p.index_or_constraint? } 63 end
# File lib/active_graph/shared/declared_properties.rb 157 def inject_defaults!(object, props) 158 declared_property_defaults.each_pair do |k, v| 159 props[k.to_sym] = v.respond_to?(:call) ? v.call : v if object.send(k).nil? && props[k.to_sym].nil? 160 end 161 props 162 end
# File lib/active_graph/shared/declared_properties.rb 131 def magic_typecast_properties 132 @magic_typecast_properties ||= {} 133 end
# File lib/active_graph/shared/declared_properties.rb 127 def magic_typecast_properties_keys 128 @magic_typecast_properties_keys ||= magic_typecast_properties.keys 129 end
# File lib/active_graph/shared/declared_properties.rb 24 def property?(key) 25 registered_properties.key?(key.to_sym) 26 end
@param [ActiveGraph::Shared::DeclaredProperty] property An instance of DeclaredProperty
, created as the result of calling property on an Node
or Relationship
class. The DeclaredProperty
has specifics about the property, but registration makes the management object aware of it. This is necessary for type conversion, defaults, and inclusion in the nil and string hashes.
# File lib/active_graph/shared/declared_properties.rb 31 def register(property) 32 @_attributes_nil_hash = nil 33 @_attributes_string_map = nil 34 registered_properties[property.name] = property 35 register_magic_typecaster(property) if property.magic_typecaster 36 declared_property_defaults[property.name] = property.default_value if !property.default_value.nil? 37 end
# File lib/active_graph/shared/declared_properties.rb 57 def registered_properties 58 @_registered_properties ||= {} 59 end
# File lib/active_graph/shared/declared_properties.rb 109 def serialize(name, coder = JSON) 110 @serialize ||= {} 111 @serialize[name] = coder 112 end
# File lib/active_graph/shared/declared_properties.rb 119 def serialized_properties 120 @serialize ||= {} 121 end
# File lib/active_graph/shared/declared_properties.rb 114 def serialized_properties=(serialize_hash) 115 @serialized_property_keys = nil 116 @serialize = serialize_hash.clone 117 end
# File lib/active_graph/shared/declared_properties.rb 123 def serialized_properties_keys 124 @serialized_property_keys ||= serialized_properties.keys 125 end
@param [Symbol] k A symbol for which the String representation is sought. This might seem silly – we could just call to_s – but when this happens many times while loading many objects, it results in a surprisingly significant slowdown. The branching logic handles what happens if a property can't be found. The first option attempts to find it in the existing hash. The second option checks whether the key is the class's id property and, if it is, the string hash is rebuilt with it to prevent future lookups. The third calls `to_s`. This would happen if undeclared properties are found on the object. We could add them to the string map but that would result in unchecked, un-GCed memory consumption. In the event that someone is adding properties dynamically, maybe through user input, this would be bad.
# File lib/active_graph/shared/declared_properties.rb 97 def string_key(k) 98 attributes_string_map[k] || string_map_id_property(k) || k.to_s 99 end
# File lib/active_graph/shared/declared_properties.rb 101 def unregister(name) 102 # might need to be include?(name.to_s) 103 fail ArgumentError, "Argument `#{name}` not an attribute" if not registered_properties[name] 104 registered_properties.delete(name) 105 unregister_magic_typecaster(name) 106 unregister_property_default(name) 107 end
# File lib/active_graph/shared/declared_properties.rb 147 def value_for_db(key, value) 148 return value unless registered_properties[key] 149 convert_property(key, value, :to_db) 150 end
# File lib/active_graph/shared/declared_properties.rb 152 def value_for_ruby(key, value) 153 return unless registered_properties[key] 154 convert_property(key, value, :to_ruby) 155 end
# File lib/active_graph/shared/declared_properties.rb 136 def value_for_where(key, value) 137 return value unless prop = registered_properties[key] 138 return value_for_db(key, value) if prop.typecaster && prop.typecaster.convert_type == value.class 139 140 if should_convert_for_where?(key, value) 141 value_for_db(key, value) 142 else 143 value 144 end 145 end
Protected Instance Methods
Prevents repeated calls to :_attribute_type, which isn't free and never changes.
# File lib/active_graph/shared/declared_properties.rb 167 def fetch_upstream_primitive(attr) 168 registered_properties[attr].type 169 end
Private Instance Methods
# File lib/active_graph/shared/declared_properties.rb 199 def register_magic_typecaster(property) 200 magic_typecast_properties[property.name] = property.magic_typecaster 201 @magic_typecast_properties_keys = nil 202 end
# File lib/active_graph/shared/declared_properties.rb 173 def should_convert_for_where?(key, value) 174 (value.is_a?(Array) && supports_array?(key)) || !EXCLUDED_TYPES.include?(value.class) 175 end
@param [Symbol] key An undeclared property value found in the _persisted_obj.properties hash. Typically, this is a node's id property, which will not be registered as other properties are. In the future, this should probably be reworked a bit. This class should either not know or care about the id property or it should be in charge of it. In the meantime, this improves node load performance.
# File lib/active_graph/shared/declared_properties.rb 182 def string_map_id_property(key) 183 return unless klass.id_property_name == key 184 key.to_s.tap do |string_key| 185 @_attributes_string_map = attributes_string_map.dup.tap { |h| h[key] = string_key }.freeze 186 end 187 end
# File lib/active_graph/shared/declared_properties.rb 189 def unregister_magic_typecaster(property) 190 magic_typecast_properties.delete(property) 191 @magic_typecast_properties_keys = nil 192 end
# File lib/active_graph/shared/declared_properties.rb 194 def unregister_property_default(property) 195 declared_property_defaults.delete(property) 196 @_default_property_values = nil 197 end