Class: ActiveGraph::Shared::DeclaredProperties

Inherits:
Object
  • Object
show all
Includes:
TypeConverters
Defined in:
lib/active_graph/shared/declared_properties.rb

Overview

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 Activeobj modules.

See ActiveGraph::Shared::DeclaredProperty for definitions of the property objects themselves.

Constant Summary collapse

EXCLUDED_TYPES =
[Array, Range, Regexp]

Constants included from TypeConverters

TypeConverters::CONVERTERS

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from TypeConverters

#convert_properties_to, #convert_property, converter_for, formatted_for_db?, included, register_converter, #supports_array?, to_other, #typecast_attribute, typecast_attribute, typecaster_for, #typecaster_for

Constructor Details

#initialize(klass) ⇒ DeclaredProperties

Each class that includes ActiveGraph::Node or ActiveGraph::Relationship gets one instance of this class.

Parameters:


16
17
18
# File 'lib/active_graph/shared/declared_properties.rb', line 16

def initialize(klass)
  @klass = klass
end

Instance Attribute Details

#klassObject (readonly)

Returns the value of attribute klass.


11
12
13
# File 'lib/active_graph/shared/declared_properties.rb', line 11

def klass
  @klass
end

Instance Method Details

#[](key) ⇒ Object


20
21
22
# File 'lib/active_graph/shared/declared_properties.rb', line 20

def [](key)
  registered_properties[key.to_sym]
end

#attributes_nil_hashObject

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.


69
70
71
72
73
74
75
76
# File 'lib/active_graph/shared/declared_properties.rb', line 69

def attributes_nil_hash
  @_attributes_nil_hash ||= {}.tap do |attr_hash|
    registered_properties.each_pair do |k, prop_obj|
      val = prop_obj.default_value
      attr_hash[k.to_s] = val
    end
  end.freeze
end

#attributes_string_mapObject

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.


82
83
84
85
86
# File 'lib/active_graph/shared/declared_properties.rb', line 82

def attributes_string_map
  @_attributes_string_map ||= {}.tap do |attr_hash|
    attributes_nil_hash.each_key { |k| attr_hash[k.to_sym] = k }
  end.freeze
end

#constraint_or_fail!(key, id_property_name, type = :unique) ⇒ Object


45
46
47
48
49
# File 'lib/active_graph/shared/declared_properties.rb', line 45

def constraint_or_fail!(key, id_property_name, type = :unique)
  return if key == id_property_name
  fail "Cannot constraint undeclared property #{property}" unless property?(key)
  registered_properties[key].constraint!(type)
end

#declared_property_defaultsObject

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.


53
54
55
# File 'lib/active_graph/shared/declared_properties.rb', line 53

def declared_property_defaults
  @_default_property_values ||= {}
end

#index_or_fail!(key, id_property_name, type = :exact) ⇒ Object


39
40
41
42
43
# File 'lib/active_graph/shared/declared_properties.rb', line 39

def index_or_fail!(key, id_property_name, type = :exact)
  return if key == id_property_name
  fail "Cannot index undeclared property #{key}" unless property?(key)
  registered_properties[key].index!(type)
end

#indexed_propertiesObject


61
62
63
# File 'lib/active_graph/shared/declared_properties.rb', line 61

def indexed_properties
  registered_properties.select { |_, p| p.index_or_constraint? }
end

#inject_defaults!(object, props) ⇒ Object


157
158
159
160
161
162
# File 'lib/active_graph/shared/declared_properties.rb', line 157

def inject_defaults!(object, props)
  declared_property_defaults.each_pair do |k, v|
    props[k.to_sym] = v.respond_to?(:call) ? v.call : v if object.send(k).nil? && props[k.to_sym].nil?
  end
  props
end

#magic_typecast_propertiesObject


131
132
133
# File 'lib/active_graph/shared/declared_properties.rb', line 131

def magic_typecast_properties
  @magic_typecast_properties ||= {}
end

#magic_typecast_properties_keysObject


127
128
129
# File 'lib/active_graph/shared/declared_properties.rb', line 127

def magic_typecast_properties_keys
  @magic_typecast_properties_keys ||= magic_typecast_properties.keys
end

#property?(key) ⇒ Boolean

Returns:


24
25
26
# File 'lib/active_graph/shared/declared_properties.rb', line 24

def property?(key)
  registered_properties.key?(key.to_sym)
end

#register(property) ⇒ Object

#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.

Parameters:


31
32
33
34
35
36
37
# File 'lib/active_graph/shared/declared_properties.rb', line 31

def register(property)
  @_attributes_nil_hash = nil
  @_attributes_string_map = nil
  registered_properties[property.name] = property
  register_magic_typecaster(property) if property.magic_typecaster
  declared_property_defaults[property.name] = property.default_value if !property.default_value.nil?
end

#registered_propertiesObject


57
58
59
# File 'lib/active_graph/shared/declared_properties.rb', line 57

def registered_properties
  @_registered_properties ||= {}
end

#serialize(name, coder = JSON) ⇒ Object


109
110
111
112
# File 'lib/active_graph/shared/declared_properties.rb', line 109

def serialize(name, coder = JSON)
  @serialize ||= {}
  @serialize[name] = coder
end

#serialized_propertiesObject


119
120
121
# File 'lib/active_graph/shared/declared_properties.rb', line 119

def serialized_properties
  @serialize ||= {}
end

#serialized_properties=(serialize_hash) ⇒ Object


114
115
116
117
# File 'lib/active_graph/shared/declared_properties.rb', line 114

def serialized_properties=(serialize_hash)
  @serialized_property_keys = nil
  @serialize = serialize_hash.clone
end

#serialized_properties_keysObject


123
124
125
# File 'lib/active_graph/shared/declared_properties.rb', line 123

def serialized_properties_keys
  @serialized_property_keys ||= serialized_properties.keys
end

#string_key(k) ⇒ Object

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.

Parameters:

  • k (Symbol)

    A symbol for which the String representation is sought. This might seem silly – we could just call #to_s –


97
98
99
# File 'lib/active_graph/shared/declared_properties.rb', line 97

def string_key(k)
  attributes_string_map[k] || string_map_id_property(k) || k.to_s
end

#unregister(name) ⇒ Object


101
102
103
104
105
106
107
# File 'lib/active_graph/shared/declared_properties.rb', line 101

def unregister(name)
  # might need to be include?(name.to_s)
  fail ArgumentError, "Argument `#{name}` not an attribute" if not registered_properties[name]
  registered_properties.delete(name)
  unregister_magic_typecaster(name)
  unregister_property_default(name)
end

#value_for_db(key, value) ⇒ Object


147
148
149
150
# File 'lib/active_graph/shared/declared_properties.rb', line 147

def value_for_db(key, value)
  return value unless registered_properties[key]
  convert_property(key, value, :to_db)
end

#value_for_ruby(key, value) ⇒ Object


152
153
154
155
# File 'lib/active_graph/shared/declared_properties.rb', line 152

def value_for_ruby(key, value)
  return unless registered_properties[key]
  convert_property(key, value, :to_ruby)
end

#value_for_where(key, value) ⇒ Object


136
137
138
139
140
141
142
143
144
145
# File 'lib/active_graph/shared/declared_properties.rb', line 136

def value_for_where(key, value)
  return value unless prop = registered_properties[key]
  return value_for_db(key, value) if prop.typecaster && prop.typecaster.convert_type == value.class

  if should_convert_for_where?(key, value)
    value_for_db(key, value)
  else
    value
  end
end