lib/neo4j/active_node/property.rb in neo4j-3.0.0.alpha.9 vs lib/neo4j/active_node/property.rb in neo4j-3.0.0.alpha.10
- old
+ new
@@ -1,212 +1,19 @@
module Neo4j::ActiveNode
module Property
extend ActiveSupport::Concern
+ include Neo4j::Shared::Property
- include ActiveAttr::Attributes
- include ActiveAttr::MassAssignment
- include ActiveAttr::TypecastedAttributes
- include ActiveAttr::AttributeDefaults
- include ActiveAttr::QueryAttributes
- include ActiveModel::Dirty
-
- class UndefinedPropertyError < RuntimeError; end
- class MultiparameterAssignmentError < StandardError; end
-
- def initialize(attributes={}, options={})
- attributes = process_attributes(attributes)
-
- writer_method_props = extract_writer_methods!(attributes)
- validate_attributes!(attributes)
- writer_method_props.each do |key, value|
- self.send("#{key}=", value)
- end
-
- super(attributes, options)
- end
-
- # Returning nil when we get ActiveAttr::UnknownAttributeError from ActiveAttr
- def read_attribute(name)
- super(name)
- rescue ActiveAttr::UnknownAttributeError
- nil
- end
- alias_method :[], :read_attribute
-
- def default_properties=(properties)
- keys = self.class.default_properties.keys
- @default_properties = properties.reject{|key| !keys.include?(key)}
- end
-
- def default_property(key)
- keys = self.class.default_properties.keys
- keys.include?(key.to_sym) ? default_properties[key.to_sym] : nil
- end
-
- def default_properties
- @default_properties ||= {}
- # keys = self.class.default_properties.keys
- # _persisted_node.props.reject{|key| !keys.include?(key)}
- end
-
-
- private
-
- # Changes attributes hash to remove relationship keys
- # Raises an error if there are any keys left which haven't been defined as properties on the model
- def validate_attributes!(attributes)
- invalid_properties = attributes.keys.map(&:to_s) - self.attributes.keys
- raise UndefinedPropertyError, "Undefined properties: #{invalid_properties.join(',')}" if invalid_properties.size > 0
- end
-
- def extract_writer_methods!(attributes)
- attributes.keys.inject({}) do |writer_method_props, key|
- writer_method_props[key] = attributes.delete(key) if self.respond_to?("#{key}=")
-
- writer_method_props
- end
- end
-
- # Gives support for Rails date_select, datetime_select, time_select helpers.
- def process_attributes(attributes = nil)
- multi_parameter_attributes = {}
- new_attributes = {}
- attributes.each_pair do |key, value|
- if key =~ /\A([^\(]+)\((\d+)([if])\)$/
- found_key, index = $1, $2.to_i
- (multi_parameter_attributes[found_key] ||= {})[index] = value.empty? ? nil : value.send("to_#{$3}")
- else
- new_attributes[key] = value
- end
- end
-
- multi_parameter_attributes.empty? ? new_attributes : process_multiparameter_attributes(multi_parameter_attributes, new_attributes)
- end
-
- def process_multiparameter_attributes(multi_parameter_attributes, new_attributes)
- multi_parameter_attributes.each_pair do |key, values|
- begin
- values = (values.keys.min..values.keys.max).map { |i| values[i] }
- field = self.class.attributes[key.to_sym]
- new_attributes[key] = instantiate_object(field, values)
- rescue => e
- raise MultiparameterAssignmentError, "error on assignment #{values.inspect} to #{key}"
- end
- end
- new_attributes
- end
-
- def instantiate_object(field, values_with_empty_parameters)
- return nil if values_with_empty_parameters.all? { |v| v.nil? }
- values = values_with_empty_parameters.collect { |v| v.nil? ? 1 : v }
- klass = field[:type]
- if klass
- klass.new(*values)
- else
- values
- end
- end
-
module ClassMethods
- # Defines a property on the class
- #
- # See active_attr gem for allowed options, e.g which type
- # Notice, in Neo4j you don't have to declare properties before using them, see the neo4j-core api.
- #
- # @example Without type
- # class Person
- # # declare a property which can have any value
- # property :name
- # end
- #
- # @example With type and a default value
- # class Person
- # # declare a property which can have any value
- # property :score, type: Integer, default: 0
- # end
- #
- # @example With an index
- # class Person
- # # declare a property which can have any value
- # property :name, index: :exact
- # end
- #
- # @example With a constraint
- # class Person
- # # declare a property which can have any value
- # property :name, constraint: :unique
- # end
- def property(name, options={})
- magic_properties(name, options)
- attribute(name, options)
-
- # either constraint or index, do not set both
- if options[:constraint]
- raise "unknown constraint type #{options[:constraint]}, only :unique supported" if options[:constraint] != :unique
- constraint(name, type: :unique)
- elsif options[:index]
- raise "unknown index type #{options[:index]}, only :exact supported" if options[:index] != :exact
- index(name, options) if options[:index] == :exact
- end
- end
-
- def default_property(name, &block)
- default_properties[name] = block
- end
-
- # @return [Hash<Symbol,Proc>]
- def default_properties
- @default_property ||= {}
- end
-
- def default_property_values(instance)
- default_properties.inject({}) do |result,pair|
- result.tap{|obj| obj[pair[0]] = pair[1].call(instance)}
- end
- end
-
- def attribute!(name, options={})
- super(name, options)
- define_method("#{name}=") do |value|
- typecast_value = typecast_attribute(typecaster_for(self.class._attribute_type(name)), value)
- send("#{name}_will_change!") unless typecast_value == read_attribute(name)
- super(value)
- end
- end
-
- def cached_class?
- !!Neo4j::Config[:cache_class_names]
- end
-
# Extracts keys from attributes hash which are relationships of the model
# TODO: Validate separately that relationships are getting the right values? Perhaps also store the values and persist relationships on save?
def extract_association_attributes!(attributes)
attributes.keys.inject({}) do |association_props, key|
association_props[key] = attributes.delete(key) if self.has_association?(key)
association_props
end
end
-
- private
-
- # Tweaks properties
- def magic_properties(name, options)
- set_stamp_type(name, options)
- set_time_as_datetime(options)
- end
-
- def set_stamp_type(name, options)
- options[:type] = DateTime if (name.to_sym == :created_at || name.to_sym == :updated_at)
- end
-
- # ActiveAttr does not handle "Time", Rails and Neo4j.rb 2.3 did
- # Convert it to DateTime in the interest of consistency
- def set_time_as_datetime(options)
- options[:type] = DateTime if options[:type] == Time
- end
-
end
end
-
end