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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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