module Neo4j::Rails::Attributes

This module handles the getting, setting and updating of attributes or properties in a Railsy way. This typically means not writing anything to the DB until the object is saved (after validation).

Externally, when we talk about properties (e.g. #property?, #property_names, properties), we mean all of the stored properties for this object include the ‘hidden’ props with underscores at the beginning such as _neo_id and _classname. When we talk about attributes, we mean all the properties apart from those hidden ones.

Public Instance Methods

_classname() click to toggle source
# File lib/neo4j/rails/attributes.rb, line 220
def _classname
  self.class.to_s
end
_classname=(value) click to toggle source
# File lib/neo4j/rails/attributes.rb, line 224
def _classname=(value)
  write_local_property_without_type_conversion("_classname",value)
end
assign_multiparameter_attributes(pairs) click to toggle source

Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done by calling new on the column type or aggregation type (through composed_of) object with these parameters. So having the pairs written_on(1) = “2004”, written_on(2) = “6”, written_on(3) = “24”, will instantiate written_on (a date type) with Date.new(“2004”, “6”, “24”). You can also specify a typecast character in the parentheses to have the parameters typecasted before they’re used in the constructor. Use i for Fixnum, f for Float, s for String, and a for Array. If all the values for a given attribute are empty, the attribute will be set to nil.

# File lib/neo4j/rails/attributes.rb, line 80
def assign_multiparameter_attributes(pairs)
  execute_callstack_for_multiparameter_attributes(
    extract_callstack_for_multiparameter_attributes(pairs)
  )
end
attribute?(name) click to toggle source

Return true if method_name is the name of an appropriate attribute method

# File lib/neo4j/rails/attributes.rb, line 216
def attribute?(name)
  name[0] != __ && property?(name)
end
attribute_names() click to toggle source

Known attributes are either in the @properties, the declared attributes or the property keys for the persisted node. Any attributes that start with _ are rejected

# File lib/neo4j/rails/attributes.rb, line 197
def attribute_names
  property_names.reject { |property_name| property_name[0] == __ }
end
attributes() click to toggle source

Return all the attributes for this model as a hash attr => value. Doesn’t include properties that start with _.

# File lib/neo4j/rails/attributes.rb, line 178
def attributes
  ret = {}
  attribute_names.each do |attribute_name|
    ret[attribute_name] = respond_to?(attribute_name) ? send(attribute_name) : send(:[], attribute_name)
  end
  ret
end
attributes=(attributes, guard_protected_attributes = true) click to toggle source

Mass-assign attributes. Stops any protected attributes from being assigned.

# File lib/neo4j/rails/attributes.rb, line 58
def attributes=(attributes, guard_protected_attributes = true)
  attributes = sanitize_for_mass_assignment(attributes) if guard_protected_attributes

  multi_parameter_attributes = []
  attributes.each do |k, v|
    if k.to_s.include?("(")
      multi_parameter_attributes << [ k, v ]
    else
      respond_to?("#{k}=") ? send("#{k}=", v) : self[k] = v
    end
  end

  assign_multiparameter_attributes(multi_parameter_attributes)
end
clear_changes() click to toggle source

Tracks the current changes and clears the changed attributes hash. Called after saving the object.

# File lib/neo4j/rails/attributes.rb, line 161
def clear_changes
  @previously_changed = changes
  @changed_attributes.clear
end
execute_callstack_for_multiparameter_attributes(callstack) click to toggle source
# File lib/neo4j/rails/attributes.rb, line 86
def execute_callstack_for_multiparameter_attributes(callstack)
  errors = []
  callstack.each do |name, values_with_empty_parameters|
    begin
      # (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
      decl_type = self.class._decl_props[name.to_sym][:type]
      raise "Not a multiparameter attribute, missing :type on property #{name} for #{self.class}" unless decl_type

      # in order to allow a date to be set without a year, we must keep the empty values.
      values = values_with_empty_parameters.reject { |v| v.nil? }

      if values.empty?
        send(name + "=", nil)
      else

        value = if :time == decl_type
          instantiate_time_object(name, values)
        elsif :date == decl_type
          begin
            values = values_with_empty_parameters.collect do |v| v.nil? ? 1 : v end
            Date.new(*values)
          rescue ArgumentError => ex # if Date.new raises an exception on an invalid date
            instantiate_time_object(name, values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
          end
        elsif :datetime == decl_type
          DateTime.new(*values)
        else
          raise "Unknown type #{decl_type}"
        end

        send(name + "=", value)
      end
    rescue Exception => ex
      raise "error on assignment #{values.inspect} to #{name}, ex: #{ex}"
    end
  end
  unless errors.empty?
    raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
  end
end
extract_callstack_for_multiparameter_attributes(pairs) click to toggle source
# File lib/neo4j/rails/attributes.rb, line 135
def extract_callstack_for_multiparameter_attributes(pairs)
  attributes = { }

  for pair in pairs
    multiparameter_name, value = pair
    attribute_name = multiparameter_name.split("(").first
    attributes[attribute_name] = [] unless attributes.include?(attribute_name)

    parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
    attributes[attribute_name] << [ find_parameter_position(multiparameter_name), parameter_value ]
  end

  attributes.each { |name, values| attributes[name] = values.sort_by{ |v| v.first }.collect { |v| v.last } }
end
find_parameter_position(multiparameter_name) click to toggle source
# File lib/neo4j/rails/attributes.rb, line 155
def find_parameter_position(multiparameter_name)
  multiparameter_name.scan(/\(([0-9]*).*\)/).first.first
end
instantiate_time_object(name, values) click to toggle source
# File lib/neo4j/rails/attributes.rb, line 127
      def instantiate_time_object(name, values)
#        if self.class.send(:create_time_zone_conversion_attribute?, name, column_for_attribute(name))
#          Time.zone.local(*values)
#        else
          Time.time_with_datetime_fallback(self.class.default_timezone, *values)
#        end
      end
method_missing(method_id, *args, &block) click to toggle source

To get ActiveModel::Dirty to work, we need to be able to call undeclared properties as though they have get methods

# File lib/neo4j/rails/attributes.rb, line 230
def method_missing(method_id, *args, &block)
  method_name = method_id.to_s
  if property?(method_name)
    self[method_name]
  else
    super
  end
end
property?(name) click to toggle source

Known properties are either in the @properties, the declared properties or the property keys for the persisted node

# File lib/neo4j/rails/attributes.rb, line 203
def property?(name)
  @properties.has_key?(name) ||
      self.class._decl_props.has_key?(name) ||
      begin
        persisted? && super
      rescue org.neo4j.graphdb.NotFoundException
        set_deleted_properties
        nil
      end
end
property_names() click to toggle source

Known properties are either in the @properties, the declared attributes or the property keys for the persisted node.

# File lib/neo4j/rails/attributes.rb, line 188
def property_names
  keys = @properties.keys + self.class._decl_props.keys.map { |k| k.to_s }
  keys += _java_entity.property_keys.to_a if persisted?
  keys.flatten.uniq
end
props() click to toggle source

Return the properties from the Neo4j Node, merged with those that haven’t yet been saved

# File lib/neo4j/rails/attributes.rb, line 168
def props
  ret = {}
  property_names.each do |property_name|
    ret[property_name] = respond_to?(property_name) ? send(property_name) : send(:[], property_name)
  end
  ret
end
read_local_property(key) click to toggle source

Returns the locally stored value for the key or retrieves the value from the DB if we don’t have one

# File lib/neo4j/rails/attributes.rb, line 48
def read_local_property(key)
  key = key.to_s
  if @properties.has_key?(key)
    @properties[key]
  else
    @properties[key] = (persisted? && _java_entity.has_property?(key)) ? read_attribute(key) : attribute_defaults[key]
  end
end
read_local_property_with_type_conversion(property) click to toggle source

Wrap the getter in a conversion from Java to Ruby

# File lib/neo4j/rails/attributes.rb, line 249
def read_local_property_with_type_conversion(property)
  Neo4j::TypeConverters.to_ruby(self.class, property, read_local_property_without_type_conversion(property))
end
respond_to?(method_id, include_private = false) click to toggle source
# File lib/neo4j/rails/attributes.rb, line 239
def respond_to?(method_id, include_private = false)
  method_name = method_id.to_s
  if property?(method_name)
    true
  else
    super
  end
end
type_cast_attribute_value(multiparameter_name, value) click to toggle source
# File lib/neo4j/rails/attributes.rb, line 151
def type_cast_attribute_value(multiparameter_name, value)
  multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
end
write_local_property(key, value) click to toggle source

The behaviour of []= changes with a Rails Model, where nothing gets written to Neo4j until the object is saved, during which time all the validations and callbacks are run to ensure correctness

# File lib/neo4j/rails/attributes.rb, line 37
def write_local_property(key, value)
  key_s = key.to_s
  if !@properties.has_key?(key_s) || @properties[key_s] != value
    attribute_will_change!(key_s)
    @properties[key_s] = value
  end
  value
end
write_local_property_with_type_conversion(property, value) click to toggle source

Wrap the setter in a conversion from Ruby to Java

# File lib/neo4j/rails/attributes.rb, line 254
def write_local_property_with_type_conversion(property, value)
  @properties_before_type_cast[property.to_sym]=value if self.class._decl_props.has_key? property.to_sym
  write_local_property_without_type_conversion(property, Neo4j::TypeConverters.to_java(self.class, property, value))
end