neo4j 3.0.0.alpha.9 → 3.0.0.alpha.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,220 @@
1
+ module Neo4j::Shared
2
+ module Property
3
+ extend ActiveSupport::Concern
4
+
5
+ include ActiveAttr::Attributes
6
+ include ActiveAttr::MassAssignment
7
+ include ActiveAttr::TypecastedAttributes
8
+ include ActiveAttr::AttributeDefaults
9
+ include ActiveAttr::QueryAttributes
10
+ include ActiveModel::Dirty
11
+
12
+ class UndefinedPropertyError < RuntimeError; end
13
+ class MultiparameterAssignmentError < StandardError; end
14
+ class IllegalPropertyError < StandardError; end
15
+
16
+ ILLEGAL_PROPS = %w[from_node to_node start_node end_node]
17
+
18
+ def initialize(attributes={}, options={})
19
+ attributes = process_attributes(attributes)
20
+ relationship_props = self.class.extract_association_attributes!(attributes)
21
+ writer_method_props = extract_writer_methods!(attributes)
22
+ validate_attributes!(attributes)
23
+ send_props(writer_method_props) unless writer_method_props.nil?
24
+ send_props(relationship_props) unless relationship_props.nil?
25
+
26
+ super(attributes, options)
27
+ end
28
+
29
+ # Returning nil when we get ActiveAttr::UnknownAttributeError from ActiveAttr
30
+ def read_attribute(name)
31
+ super(name)
32
+ rescue ActiveAttr::UnknownAttributeError
33
+ nil
34
+ end
35
+ alias_method :[], :read_attribute
36
+
37
+ def default_properties=(properties)
38
+ keys = self.class.default_properties.keys
39
+ @default_properties = properties.reject{|key| !keys.include?(key)}
40
+ end
41
+
42
+ def default_property(key)
43
+ keys = self.class.default_properties.keys
44
+ keys.include?(key.to_sym) ? default_properties[key.to_sym] : nil
45
+ end
46
+
47
+ def default_properties
48
+ @default_properties ||= {}
49
+ # keys = self.class.default_properties.keys
50
+ # _persisted_obj.props.reject{|key| !keys.include?(key)}
51
+ end
52
+
53
+ def send_props(hash)
54
+ hash.each do |key, value|
55
+ self.send("#{key}=", value)
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ # Changes attributes hash to remove relationship keys
62
+ # Raises an error if there are any keys left which haven't been defined as properties on the model
63
+ def validate_attributes!(attributes)
64
+ invalid_properties = attributes.keys.map(&:to_s) - self.attributes.keys
65
+ raise UndefinedPropertyError, "Undefined properties: #{invalid_properties.join(',')}" if invalid_properties.size > 0
66
+ end
67
+
68
+ def extract_writer_methods!(attributes)
69
+ attributes.keys.inject({}) do |writer_method_props, key|
70
+ writer_method_props[key] = attributes.delete(key) if self.respond_to?("#{key}=")
71
+
72
+ writer_method_props
73
+ end
74
+ end
75
+
76
+ # Gives support for Rails date_select, datetime_select, time_select helpers.
77
+ def process_attributes(attributes = nil)
78
+ multi_parameter_attributes = {}
79
+ new_attributes = {}
80
+ attributes.each_pair do |key, value|
81
+ if key =~ /\A([^\(]+)\((\d+)([if])\)$/
82
+ found_key, index = $1, $2.to_i
83
+ (multi_parameter_attributes[found_key] ||= {})[index] = value.empty? ? nil : value.send("to_#{$3}")
84
+ else
85
+ new_attributes[key] = value
86
+ end
87
+ end
88
+
89
+ multi_parameter_attributes.empty? ? new_attributes : process_multiparameter_attributes(multi_parameter_attributes, new_attributes)
90
+ end
91
+
92
+ def process_multiparameter_attributes(multi_parameter_attributes, new_attributes)
93
+ multi_parameter_attributes.each_pair do |key, values|
94
+ begin
95
+ values = (values.keys.min..values.keys.max).map { |i| values[i] }
96
+ field = self.class.attributes[key.to_sym]
97
+ new_attributes[key] = instantiate_object(field, values)
98
+ rescue => e
99
+ raise MultiparameterAssignmentError, "error on assignment #{values.inspect} to #{key}"
100
+ end
101
+ end
102
+ new_attributes
103
+ end
104
+
105
+ def instantiate_object(field, values_with_empty_parameters)
106
+ return nil if values_with_empty_parameters.all? { |v| v.nil? }
107
+ values = values_with_empty_parameters.collect { |v| v.nil? ? 1 : v }
108
+ klass = field[:type]
109
+ if klass
110
+ klass.new(*values)
111
+ else
112
+ values
113
+ end
114
+ end
115
+
116
+ module ClassMethods
117
+
118
+ # Defines a property on the class
119
+ #
120
+ # See active_attr gem for allowed options, e.g which type
121
+ # Notice, in Neo4j you don't have to declare properties before using them, see the neo4j-core api.
122
+ #
123
+ # @example Without type
124
+ # class Person
125
+ # # declare a property which can have any value
126
+ # property :name
127
+ # end
128
+ #
129
+ # @example With type and a default value
130
+ # class Person
131
+ # # declare a property which can have any value
132
+ # property :score, type: Integer, default: 0
133
+ # end
134
+ #
135
+ # @example With an index
136
+ # class Person
137
+ # # declare a property which can have any value
138
+ # property :name, index: :exact
139
+ # end
140
+ #
141
+ # @example With a constraint
142
+ # class Person
143
+ # # declare a property which can have any value
144
+ # property :name, constraint: :unique
145
+ # end
146
+ def property(name, options={})
147
+ check_illegal_prop(name)
148
+ magic_properties(name, options)
149
+ attribute(name, options)
150
+ constraint_or_index(name, options)
151
+ end
152
+
153
+ def default_property(name, &block)
154
+ default_properties[name] = block
155
+ end
156
+
157
+ # @return [Hash<Symbol,Proc>]
158
+ def default_properties
159
+ @default_property ||= {}
160
+ end
161
+
162
+ def default_property_values(instance)
163
+ default_properties.inject({}) do |result,pair|
164
+ result.tap{|obj| obj[pair[0]] = pair[1].call(instance)}
165
+ end
166
+ end
167
+
168
+ def attribute!(name, options={})
169
+ super(name, options)
170
+ define_method("#{name}=") do |value|
171
+ typecast_value = typecast_attribute(typecaster_for(self.class._attribute_type(name)), value)
172
+ send("#{name}_will_change!") unless typecast_value == read_attribute(name)
173
+ super(value)
174
+ end
175
+ end
176
+
177
+ def cached_class?
178
+ !!Neo4j::Config[:cache_class_names]
179
+ end
180
+
181
+ private
182
+
183
+ def constraint_or_index(name, options)
184
+ # either constraint or index, do not set both
185
+ if options[:constraint]
186
+ raise "unknown constraint type #{options[:constraint]}, only :unique supported" if options[:constraint] != :unique
187
+ constraint(name, type: :unique)
188
+ elsif options[:index]
189
+ raise "unknown index type #{options[:index]}, only :exact supported" if options[:index] != :exact
190
+ index(name, options) if options[:index] == :exact
191
+ end
192
+ end
193
+
194
+ def check_illegal_prop(name)
195
+ if ILLEGAL_PROPS.include?(name.to_s)
196
+ raise IllegalPropertyError, "#{name} is an illegal property"
197
+ end
198
+ end
199
+
200
+ # Tweaks properties
201
+ def magic_properties(name, options)
202
+ set_stamp_type(name, options)
203
+ set_time_as_datetime(options)
204
+ end
205
+
206
+ def set_stamp_type(name, options)
207
+ options[:type] = DateTime if (name.to_sym == :created_at || name.to_sym == :updated_at)
208
+ end
209
+
210
+ # ActiveAttr does not handle "Time", Rails and Neo4j.rb 2.3 did
211
+ # Convert it to DateTime in the interest of consistency
212
+ def set_time_as_datetime(options)
213
+ options[:type] = DateTime if options[:type] == Time
214
+ end
215
+
216
+ end
217
+
218
+ end
219
+
220
+ end
@@ -0,0 +1,48 @@
1
+ module Neo4j
2
+ module Shared
3
+ module Validations
4
+ extend ActiveSupport::Concern
5
+ include ActiveModel::Validations
6
+ # Implements the ActiveModel::Validation hook method.
7
+ # @see http://rubydoc.info/docs/rails/ActiveModel/Validations:read_attribute_for_validation
8
+ def read_attribute_for_validation(key)
9
+ respond_to?(key) ? send(key) : self[key]
10
+ end
11
+
12
+ # The validation process on save can be skipped by passing false. The regular Model#save method is
13
+ # replaced with this when the validations module is mixed in, which it is by default.
14
+ # @param [Hash] options the options to create a message with.
15
+ # @option options [true, false] :validate if false no validation will take place
16
+ # @return [Boolean] true if it saved it successfully
17
+ def save(options={})
18
+ result = perform_validations(options) ? super : false
19
+ if !result
20
+ Neo4j::Transaction.current.failure if Neo4j::Transaction.current
21
+ end
22
+ result
23
+ end
24
+
25
+ # @return [Boolean] true if valid
26
+ def valid?(context = nil)
27
+ context ||= (new_record? ? :create : :update)
28
+ super(context)
29
+ errors.empty?
30
+ end
31
+
32
+ private
33
+
34
+ def perform_validations(options={})
35
+ perform_validation = case options
36
+ when Hash
37
+ options[:validate] != false
38
+ end
39
+
40
+ if perform_validation
41
+ valid?(options.is_a?(Hash) ? options[:context] : nil)
42
+ else
43
+ true
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
data/lib/neo4j/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Neo4j
2
- VERSION = "3.0.0.alpha.9"
2
+ VERSION = "3.0.0.alpha.10"
3
3
  end
data/lib/neo4j/wrapper.rb CHANGED
@@ -1,52 +1,4 @@
1
- class Neo4j::Node
2
-
3
- module Wrapper
4
-
5
- # this is a plugin in the neo4j-core so that the Ruby wrapper will be wrapped around the Neo4j::Node objects
6
- def wrapper
7
- most_concrete_class = sorted_wrapper_classes
8
- wrapped_node = most_concrete_class.new
9
- wrapped_node.init_on_load(self, self.props)
10
- wrapped_node
11
- end
12
-
13
- def checked_labels_set
14
- @@_checked_labels_set ||= Set.new
15
- end
16
-
17
- def check_label(label_name)
18
- unless checked_labels_set.include?(label_name)
19
- load_class_from_label(label_name)
20
- # do this only once
21
- checked_labels_set.add(label_name)
22
- end
23
- end
24
-
25
- def sorted_wrapper_classes
26
- if self.props.is_a?(Hash) && self.props.has_key?(Neo4j::Config.class_name_property)
27
- self.props[Neo4j::Config.class_name_property].constantize
28
- else
29
- wrappers = _class_wrappers
30
- return self if wrappers.nil?
31
- wrapper_classes = wrappers.map{|w| Neo4j::ActiveNode::Labels._wrapped_labels[w]}
32
- wrapper_classes.sort.first
33
- end
34
- end
35
-
36
- def load_class_from_label(label_name)
37
- begin
38
- label_name.to_s.split("::").inject(Kernel) {|container, name| container.const_get(name.to_s) }
39
- rescue NameError
40
- nil
41
- end
42
- end
43
-
44
- def _class_wrappers
45
- labels.find_all do |label_name|
46
- check_label(label_name)
47
- Neo4j::ActiveNode::Labels._wrapped_labels[label_name].class == Class
48
- end
49
- end
1
+ module Neo4j
2
+ module ClassWrapper
50
3
  end
51
-
52
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: neo4j
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0.alpha.9
4
+ version: 3.0.0.alpha.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Ronge
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-14 00:00:00.000000000 Z
11
+ date: 2014-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: orm_adapter
@@ -119,23 +119,36 @@ files:
119
119
  - lib/neo4j/active_node/callbacks.rb
120
120
  - lib/neo4j/active_node/has_n.rb
121
121
  - lib/neo4j/active_node/has_n/association.rb
122
- - lib/neo4j/active_node/has_n/nodes.rb
123
122
  - lib/neo4j/active_node/id_property.rb
124
- - lib/neo4j/active_node/identity.rb
125
123
  - lib/neo4j/active_node/initialize.rb
126
124
  - lib/neo4j/active_node/labels.rb
125
+ - lib/neo4j/active_node/node_wrapper.rb
127
126
  - lib/neo4j/active_node/orm_adapter.rb
128
127
  - lib/neo4j/active_node/persistence.rb
129
128
  - lib/neo4j/active_node/property.rb
130
129
  - lib/neo4j/active_node/query.rb
131
130
  - lib/neo4j/active_node/query/query_proxy.rb
132
- - lib/neo4j/active_node/quick_query.rb
133
131
  - lib/neo4j/active_node/rels.rb
134
132
  - lib/neo4j/active_node/serialized_properties.rb
135
133
  - lib/neo4j/active_node/validations.rb
134
+ - lib/neo4j/active_rel.rb
135
+ - lib/neo4j/active_rel/callbacks.rb
136
+ - lib/neo4j/active_rel/initialize.rb
137
+ - lib/neo4j/active_rel/persistence.rb
138
+ - lib/neo4j/active_rel/property.rb
139
+ - lib/neo4j/active_rel/query.rb
140
+ - lib/neo4j/active_rel/rel_wrapper.rb
141
+ - lib/neo4j/active_rel/related_node.rb
142
+ - lib/neo4j/active_rel/validations.rb
136
143
  - lib/neo4j/config.rb
137
144
  - lib/neo4j/paginated.rb
138
145
  - lib/neo4j/railtie.rb
146
+ - lib/neo4j/shared.rb
147
+ - lib/neo4j/shared/callbacks.rb
148
+ - lib/neo4j/shared/identity.rb
149
+ - lib/neo4j/shared/persistence.rb
150
+ - lib/neo4j/shared/property.rb
151
+ - lib/neo4j/shared/validations.rb
139
152
  - lib/neo4j/type_converters.rb
140
153
  - lib/neo4j/version.rb
141
154
  - lib/neo4j/wrapper.rb
@@ -1,90 +0,0 @@
1
- module Neo4j
2
- module ActiveNode
3
- module HasN
4
-
5
- # The object created by a has_n or has_one Neo4j::NodeMixin class method which enables creating and traversal of nodes.
6
- #
7
- # @see Neo4j::ActiveNode::HasN::ClassMethods
8
- class Nodes
9
- include Enumerable
10
-
11
- def initialize(node, decl_rel) # :nodoc:
12
- @node = node
13
- @decl_rel = decl_rel
14
- end
15
-
16
- def to_s
17
- "[#{@decl_rel.dir}, from: #{@node.neo_id} type: #{@decl_rel.rel_type} has_one #{@decl_rel.has_one?}]"
18
- end
19
-
20
- def inspect
21
- to_s
22
- end
23
-
24
- # Traverse the relationship till the index position
25
- # @return [Neo4j::ActiveMixin,Neo4j::Node,nil] the node at the given position
26
- def [](index)
27
- i = 0
28
- each { |x| return x if i == index; i += 1 }
29
- nil # out of index
30
- end
31
-
32
- # Pretend we are an array - this is necessarily for Rails actionpack/actionview/formhelper to work with this
33
- def is_a?(type)
34
- # ActionView requires this for nested attributes to work
35
- return true if Array == type
36
- super
37
- end
38
-
39
- def ==(other)
40
- self.to_a == other.to_a
41
- end
42
- alias_method :eql?, :==
43
-
44
- # Required by the Enumerable mixin.
45
- def each
46
- @decl_rel.each_node(@node) { |n| yield n } # Should use yield here as passing &block through doesn't always work (why?)
47
- end
48
-
49
- # returns none wrapped nodes, you may get better performance using this method
50
- def _each
51
- @decl_rel._each_node(@node) { |n| yield n }
52
- end
53
-
54
- # Returns an real ruby array.
55
- def to_ary
56
- self.to_a
57
- end
58
-
59
- # Returns true if there are no node in this type of relationship
60
- def empty?
61
- first == nil
62
- end
63
-
64
-
65
- # Creates a relationship instance between this and the other node.
66
- # Returns the relationship object
67
- def create(other, relationship_props = {})
68
- @decl_rel.create_relationship_to(@node, other, relationship_props)
69
- end
70
-
71
-
72
- # Creates a relationship between this and the other node.
73
- #
74
- # @example Person includes the Neo4j::NodeMixin and declares a has_n :friends
75
- #
76
- # p = Person.new # Node has declared having a friend type of relationship
77
- # n1 = Node.new
78
- # n2 = Node.new
79
- #
80
- # p.friends << n2 << n3
81
- #
82
- # @return self
83
- def <<(other)
84
- !@decl_rel.create_relationship_to(@node, other) ? false : self
85
- end
86
- end
87
-
88
- end
89
- end
90
- end