activegraph 10.0.0.pre.alpha.6

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.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1989 -0
  3. data/CONTRIBUTORS +12 -0
  4. data/Gemfile +24 -0
  5. data/README.md +107 -0
  6. data/bin/rake +17 -0
  7. data/config/locales/en.yml +5 -0
  8. data/config/neo4j/add_classnames.yml +1 -0
  9. data/config/neo4j/config.yml +38 -0
  10. data/lib/neo4j.rb +116 -0
  11. data/lib/neo4j/active_base.rb +89 -0
  12. data/lib/neo4j/active_node.rb +108 -0
  13. data/lib/neo4j/active_node/callbacks.rb +8 -0
  14. data/lib/neo4j/active_node/dependent.rb +11 -0
  15. data/lib/neo4j/active_node/dependent/association_methods.rb +49 -0
  16. data/lib/neo4j/active_node/dependent/query_proxy_methods.rb +51 -0
  17. data/lib/neo4j/active_node/enum.rb +26 -0
  18. data/lib/neo4j/active_node/has_n.rb +612 -0
  19. data/lib/neo4j/active_node/has_n/association.rb +278 -0
  20. data/lib/neo4j/active_node/has_n/association/rel_factory.rb +61 -0
  21. data/lib/neo4j/active_node/has_n/association/rel_wrapper.rb +23 -0
  22. data/lib/neo4j/active_node/has_n/association_cypher_methods.rb +108 -0
  23. data/lib/neo4j/active_node/id_property.rb +224 -0
  24. data/lib/neo4j/active_node/id_property/accessor.rb +62 -0
  25. data/lib/neo4j/active_node/initialize.rb +21 -0
  26. data/lib/neo4j/active_node/labels.rb +207 -0
  27. data/lib/neo4j/active_node/labels/index.rb +37 -0
  28. data/lib/neo4j/active_node/labels/reloading.rb +21 -0
  29. data/lib/neo4j/active_node/node_list_formatter.rb +13 -0
  30. data/lib/neo4j/active_node/node_wrapper.rb +54 -0
  31. data/lib/neo4j/active_node/orm_adapter.rb +82 -0
  32. data/lib/neo4j/active_node/persistence.rb +187 -0
  33. data/lib/neo4j/active_node/property.rb +60 -0
  34. data/lib/neo4j/active_node/query.rb +76 -0
  35. data/lib/neo4j/active_node/query/query_proxy.rb +374 -0
  36. data/lib/neo4j/active_node/query/query_proxy_eager_loading.rb +177 -0
  37. data/lib/neo4j/active_node/query/query_proxy_eager_loading/association_tree.rb +75 -0
  38. data/lib/neo4j/active_node/query/query_proxy_enumerable.rb +110 -0
  39. data/lib/neo4j/active_node/query/query_proxy_find_in_batches.rb +19 -0
  40. data/lib/neo4j/active_node/query/query_proxy_link.rb +139 -0
  41. data/lib/neo4j/active_node/query/query_proxy_methods.rb +302 -0
  42. data/lib/neo4j/active_node/query/query_proxy_methods_of_mass_updating.rb +86 -0
  43. data/lib/neo4j/active_node/query_methods.rb +68 -0
  44. data/lib/neo4j/active_node/reflection.rb +86 -0
  45. data/lib/neo4j/active_node/rels.rb +11 -0
  46. data/lib/neo4j/active_node/scope.rb +166 -0
  47. data/lib/neo4j/active_node/unpersisted.rb +48 -0
  48. data/lib/neo4j/active_node/validations.rb +59 -0
  49. data/lib/neo4j/active_rel.rb +67 -0
  50. data/lib/neo4j/active_rel/callbacks.rb +15 -0
  51. data/lib/neo4j/active_rel/initialize.rb +28 -0
  52. data/lib/neo4j/active_rel/persistence.rb +134 -0
  53. data/lib/neo4j/active_rel/persistence/query_factory.rb +95 -0
  54. data/lib/neo4j/active_rel/property.rb +95 -0
  55. data/lib/neo4j/active_rel/query.rb +101 -0
  56. data/lib/neo4j/active_rel/rel_wrapper.rb +31 -0
  57. data/lib/neo4j/active_rel/related_node.rb +87 -0
  58. data/lib/neo4j/active_rel/types.rb +82 -0
  59. data/lib/neo4j/active_rel/validations.rb +8 -0
  60. data/lib/neo4j/ansi.rb +14 -0
  61. data/lib/neo4j/class_arguments.rb +39 -0
  62. data/lib/neo4j/config.rb +135 -0
  63. data/lib/neo4j/core.rb +14 -0
  64. data/lib/neo4j/core/connection_failed_error.rb +6 -0
  65. data/lib/neo4j/core/cypher_error.rb +37 -0
  66. data/lib/neo4j/core/driver.rb +66 -0
  67. data/lib/neo4j/core/has_uri.rb +63 -0
  68. data/lib/neo4j/core/instrumentable.rb +36 -0
  69. data/lib/neo4j/core/label.rb +158 -0
  70. data/lib/neo4j/core/logging.rb +44 -0
  71. data/lib/neo4j/core/node.rb +23 -0
  72. data/lib/neo4j/core/querable.rb +88 -0
  73. data/lib/neo4j/core/query.rb +487 -0
  74. data/lib/neo4j/core/query_builder.rb +32 -0
  75. data/lib/neo4j/core/query_clauses.rb +727 -0
  76. data/lib/neo4j/core/query_ext.rb +24 -0
  77. data/lib/neo4j/core/query_find_in_batches.rb +49 -0
  78. data/lib/neo4j/core/relationship.rb +13 -0
  79. data/lib/neo4j/core/responses.rb +50 -0
  80. data/lib/neo4j/core/result.rb +33 -0
  81. data/lib/neo4j/core/schema.rb +30 -0
  82. data/lib/neo4j/core/schema_errors.rb +12 -0
  83. data/lib/neo4j/core/wrappable.rb +30 -0
  84. data/lib/neo4j/errors.rb +57 -0
  85. data/lib/neo4j/migration.rb +148 -0
  86. data/lib/neo4j/migrations.rb +27 -0
  87. data/lib/neo4j/migrations/base.rb +77 -0
  88. data/lib/neo4j/migrations/check_pending.rb +20 -0
  89. data/lib/neo4j/migrations/helpers.rb +105 -0
  90. data/lib/neo4j/migrations/helpers/id_property.rb +75 -0
  91. data/lib/neo4j/migrations/helpers/relationships.rb +66 -0
  92. data/lib/neo4j/migrations/helpers/schema.rb +51 -0
  93. data/lib/neo4j/migrations/migration_file.rb +24 -0
  94. data/lib/neo4j/migrations/runner.rb +195 -0
  95. data/lib/neo4j/migrations/schema.rb +44 -0
  96. data/lib/neo4j/migrations/schema_migration.rb +14 -0
  97. data/lib/neo4j/model_schema.rb +139 -0
  98. data/lib/neo4j/paginated.rb +27 -0
  99. data/lib/neo4j/railtie.rb +105 -0
  100. data/lib/neo4j/schema/operation.rb +102 -0
  101. data/lib/neo4j/shared.rb +60 -0
  102. data/lib/neo4j/shared/attributes.rb +216 -0
  103. data/lib/neo4j/shared/callbacks.rb +68 -0
  104. data/lib/neo4j/shared/cypher.rb +37 -0
  105. data/lib/neo4j/shared/declared_properties.rb +204 -0
  106. data/lib/neo4j/shared/declared_property.rb +109 -0
  107. data/lib/neo4j/shared/declared_property/index.rb +37 -0
  108. data/lib/neo4j/shared/enum.rb +167 -0
  109. data/lib/neo4j/shared/filtered_hash.rb +79 -0
  110. data/lib/neo4j/shared/identity.rb +34 -0
  111. data/lib/neo4j/shared/initialize.rb +64 -0
  112. data/lib/neo4j/shared/marshal.rb +23 -0
  113. data/lib/neo4j/shared/mass_assignment.rb +64 -0
  114. data/lib/neo4j/shared/permitted_attributes.rb +28 -0
  115. data/lib/neo4j/shared/persistence.rb +282 -0
  116. data/lib/neo4j/shared/property.rb +240 -0
  117. data/lib/neo4j/shared/query_factory.rb +102 -0
  118. data/lib/neo4j/shared/rel_type_converters.rb +43 -0
  119. data/lib/neo4j/shared/serialized_properties.rb +30 -0
  120. data/lib/neo4j/shared/type_converters.rb +433 -0
  121. data/lib/neo4j/shared/typecasted_attributes.rb +98 -0
  122. data/lib/neo4j/shared/typecaster.rb +53 -0
  123. data/lib/neo4j/shared/validations.rb +44 -0
  124. data/lib/neo4j/tasks/migration.rake +202 -0
  125. data/lib/neo4j/timestamps.rb +11 -0
  126. data/lib/neo4j/timestamps/created.rb +9 -0
  127. data/lib/neo4j/timestamps/updated.rb +9 -0
  128. data/lib/neo4j/transaction.rb +139 -0
  129. data/lib/neo4j/type_converters.rb +7 -0
  130. data/lib/neo4j/undeclared_properties.rb +53 -0
  131. data/lib/neo4j/version.rb +3 -0
  132. data/lib/neo4j/wrapper.rb +4 -0
  133. data/lib/rails/generators/neo4j/migration/migration_generator.rb +14 -0
  134. data/lib/rails/generators/neo4j/migration/templates/migration.erb +9 -0
  135. data/lib/rails/generators/neo4j/model/model_generator.rb +88 -0
  136. data/lib/rails/generators/neo4j/model/templates/migration.erb +9 -0
  137. data/lib/rails/generators/neo4j/model/templates/model.erb +15 -0
  138. data/lib/rails/generators/neo4j/upgrade_v8/templates/migration.erb +17 -0
  139. data/lib/rails/generators/neo4j/upgrade_v8/upgrade_v8_generator.rb +32 -0
  140. data/lib/rails/generators/neo4j_generator.rb +119 -0
  141. data/neo4j.gemspec +51 -0
  142. metadata +421 -0
@@ -0,0 +1,86 @@
1
+ module Neo4j::ActiveNode
2
+ # A reflection contains information about an association.
3
+ # They are often used in connection with form builders to determine associated classes.
4
+ # This module contains methods related to the creation and retrieval of reflections.
5
+ module Reflection
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :reflections
10
+ self.reflections = {}
11
+ end
12
+
13
+ # Adds methods to the class related to creating and retrieving reflections.
14
+ module ClassMethods
15
+ # @param macro [Symbol] the association type, :has_many or :has_one
16
+ # @param name [Symbol] the association name
17
+ # @param association_object [Neo4j::ActiveNode::HasN::Association] the association object created in the course of creating this reflection
18
+ def create_reflection(macro, name, association_object, model)
19
+ self.reflections = self.reflections.merge(name => AssociationReflection.new(macro, name, association_object))
20
+ association_object.add_destroy_callbacks(model)
21
+ end
22
+
23
+ private :create_reflection
24
+ # @param association [Symbol] an association declared on the model
25
+ # @return [Neo4j::ActiveNode::Reflection::AssociationReflection] of the given association
26
+ def reflect_on_association(association)
27
+ reflections[association.to_sym]
28
+ end
29
+
30
+ # Returns an array containing one reflection for each association declared in the model.
31
+ def reflect_on_all_associations(macro = nil)
32
+ association_reflections = reflections.values
33
+ macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
34
+ end
35
+ end
36
+
37
+ # The actual reflection object that contains information about the given association.
38
+ # These should never need to be created manually, they will always be created by declaring a :has_many or :has_one association on a model.
39
+ class AssociationReflection
40
+ # The name of the association
41
+ attr_reader :name
42
+
43
+ # The type of association
44
+ attr_reader :macro
45
+
46
+ # The association object referenced by this reflection
47
+ attr_reader :association
48
+
49
+ def initialize(macro, name, association)
50
+ @macro = macro
51
+ @name = name
52
+ @association = association
53
+ end
54
+
55
+ # Returns the target model
56
+ def klass
57
+ @klass ||= class_name.constantize
58
+ end
59
+
60
+ # Returns the name of the target model
61
+ def class_name
62
+ @class_name ||= association.target_class.name
63
+ end
64
+
65
+ def rel_klass
66
+ @rel_klass ||= rel_class_name.constantize
67
+ end
68
+
69
+ def rel_class_name
70
+ @rel_class_name ||= association.relationship_class.name.to_s
71
+ end
72
+
73
+ def type
74
+ @type ||= association.relationship_type
75
+ end
76
+
77
+ def collection?
78
+ macro == :has_many
79
+ end
80
+
81
+ def validate?
82
+ true
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,11 @@
1
+ module Neo4j::ActiveNode
2
+ module Rels
3
+ extend Forwardable
4
+ def_delegators :_rels_delegator, :rel?, :rel, :rels, :node, :nodes, :create_rel
5
+
6
+ def _rels_delegator
7
+ fail "Can't access relationship on a non persisted node" unless _persisted_obj
8
+ _persisted_obj
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,166 @@
1
+ require 'active_support/per_thread_registry'
2
+
3
+ module Neo4j::ActiveNode
4
+ module Scope
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ # Similar to ActiveRecord scope
9
+ #
10
+ # @example without argument
11
+ # class Person
12
+ # include Neo4j::ActiveNode
13
+ # property :name
14
+ # property :score
15
+ # has_many :out, :friends, type: :has_friend, model_class: self
16
+ # scope :top_students, -> { where(score: 42)}") }
17
+ # end
18
+ # Person.top_students.to_a
19
+ # a_person.friends.top_students.to_a
20
+ # a_person.friends.friends.top_students.to_a
21
+ # a_person.friends.top_students.friends.to_a
22
+ #
23
+ # @example Argument for scopes
24
+ # Person.scope :level, ->(num) { where(level_num: num)}
25
+ #
26
+ # @example Argument as a cypher identifier
27
+ # class Person
28
+ # include Neo4j::ActiveNode
29
+ # property :name
30
+ # property :score
31
+ # has_many :out, :friends, type: :has_friend, model_class: self
32
+ # scope :great_students, ->(identifier) { where("#{identifier}.score > 41") }
33
+ # end
34
+ # Person.as(:all_people).great_students(:all_people).to_a
35
+ #
36
+ # @see http://guides.rubyonrails.org/active_record_querying.html#scopes
37
+ def scope(name, proc)
38
+ scopes[name.to_sym] = proc
39
+
40
+ klass = class << self; self; end
41
+ klass.instance_eval do
42
+ define_method(name) do |*query_params|
43
+ eval_context = ScopeEvalContext.new(self, current_scope || self.query_proxy)
44
+ proc = full_scopes[name.to_sym]
45
+ _call_scope_context(eval_context, query_params, proc)
46
+ end
47
+ end
48
+
49
+ define_method(name) do |*query_params|
50
+ as(:n).public_send(name, *query_params)
51
+ end
52
+ end
53
+
54
+ # rubocop:disable Naming/PredicateName
55
+ def has_scope?(name)
56
+ ActiveSupport::Deprecation.warn 'has_scope? is deprecated and may be removed from future releases, use scope? instead.', caller
57
+
58
+ scope?(name)
59
+ end
60
+ # rubocop:enable Naming/PredicateName
61
+
62
+ # @return [Boolean] true if model has access to scope with this name
63
+ def scope?(name)
64
+ full_scopes.key?(name.to_sym)
65
+ end
66
+
67
+ # @return [Hash] of scopes assigned to this model. Keys are scope name, value is scope callable.
68
+ def scopes
69
+ @scopes ||= {}
70
+ end
71
+
72
+ # @return [Hash] of scopes available to this model. Keys are scope name, value is scope callable.
73
+ def full_scopes
74
+ self.ancestors.find_all { |a| a.respond_to?(:scopes) }.reverse.inject({}) do |scopes, a|
75
+ scopes.merge(a.scopes)
76
+ end
77
+ end
78
+
79
+ def _call_scope_context(eval_context, query_params, proc)
80
+ eval_context.instance_exec(*query_params.fill(nil, query_params.length..proc.arity - 1), &proc)
81
+ end
82
+
83
+ def current_scope #:nodoc:
84
+ ScopeRegistry.value_for(:current_scope, base_class.to_s)
85
+ end
86
+
87
+ def current_scope=(scope) #:nodoc:
88
+ ScopeRegistry.set_value_for(:current_scope, base_class.to_s, scope)
89
+ end
90
+
91
+ def all(new_var = nil)
92
+ var = new_var || (current_scope ? current_scope.node_identity : :n)
93
+ if current_scope
94
+ current_scope.new_link(var)
95
+ else
96
+ self.as(var)
97
+ end
98
+ end
99
+ end
100
+
101
+ class ScopeEvalContext
102
+ def initialize(target, query_proxy)
103
+ @query_proxy = query_proxy
104
+ @target = target
105
+ end
106
+
107
+ def identity
108
+ query_proxy_or_target.identity
109
+ end
110
+
111
+ Neo4j::ActiveNode::Query::QueryProxy::METHODS.each do |method|
112
+ define_method(method) do |*args|
113
+ @target.all.scoping do
114
+ query_proxy_or_target.public_send(method, *args)
115
+ end
116
+ end
117
+ end
118
+
119
+ # method_missing is not delegated to super class but to aggregated class
120
+ # rubocop:disable Style/MethodMissingSuper
121
+ def method_missing(name, *params, &block)
122
+ query_proxy_or_target.public_send(name, *params, &block)
123
+ end
124
+ # rubocop:enable Style/MethodMissingSuper
125
+
126
+ private
127
+
128
+ def query_proxy_or_target
129
+ @query_proxy_or_target ||= @query_proxy || @target
130
+ end
131
+ end
132
+
133
+
134
+ # Stolen from ActiveRecord
135
+ # https://github.com/rails/rails/blob/08754f12e65a9ec79633a605e986d0f1ffa4b251/activerecord/lib/active_record/scoping.rb#L57
136
+ class ScopeRegistry # :nodoc:
137
+ extend ActiveSupport::PerThreadRegistry
138
+
139
+ VALID_SCOPE_TYPES = [:current_scope, :ignore_default_scope]
140
+
141
+ def initialize
142
+ @registry = Hash.new { |hash, key| hash[key] = {} }
143
+ end
144
+
145
+ # Obtains the value for a given +scope_name+ and +variable_name+.
146
+ def value_for(scope_type, variable_name)
147
+ raise_invalid_scope_type!(scope_type)
148
+ @registry[scope_type][variable_name]
149
+ end
150
+
151
+ # Sets the +value+ for a given +scope_type+ and +variable_name+.
152
+ def set_value_for(scope_type, variable_name, value)
153
+ raise_invalid_scope_type!(scope_type)
154
+ @registry[scope_type][variable_name] = value
155
+ end
156
+
157
+ private
158
+
159
+ def raise_invalid_scope_type!(scope_type)
160
+ return if VALID_SCOPE_TYPES.include?(scope_type)
161
+
162
+ fail ArgumentError, "Invalid scope type '#{scope_type}' sent to the registry. Scope types must be included in VALID_SCOPE_TYPES"
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,48 @@
1
+ module Neo4j
2
+ module ActiveNode
3
+ module Unpersisted
4
+ # The values in this Hash are returned and used outside by reference
5
+ # so any modifications to the Array should be in-place
6
+ def deferred_create_cache
7
+ @deferred_create_cache ||= {}
8
+ end
9
+
10
+ def defer_create(association_name, object, options = {})
11
+ clear_deferred_nodes_for_association(association_name) if options[:clear]
12
+
13
+ deferred_nodes_for_association(association_name) << object
14
+ end
15
+
16
+ def deferred_nodes_for_association(association_name)
17
+ deferred_create_cache[association_name.to_sym] ||= []
18
+ end
19
+
20
+ def pending_deferred_creations?
21
+ !deferred_create_cache.values.all?(&:empty?)
22
+ end
23
+
24
+ def clear_deferred_nodes_for_association(association_name)
25
+ deferred_nodes_for_association(association_name.to_sym).clear
26
+ end
27
+
28
+ private
29
+
30
+ def process_unpersisted_nodes!
31
+ deferred_create_cache.dup.each do |association_name, nodes|
32
+ association_proxy = association_proxy(association_name)
33
+
34
+ nodes.each do |node|
35
+ if node.respond_to?(:changed?)
36
+ node.save if node.changed? || !node.persisted?
37
+ fail "Unable to defer node persistence, could not save #{node.inspect}" unless node.persisted?
38
+ end
39
+
40
+ association_proxy << node
41
+ end
42
+ end
43
+
44
+ @deferred_create_cache = {}
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,59 @@
1
+ module Neo4j
2
+ module ActiveNode
3
+ # This mixin replace the original save method and performs validation before the save.
4
+ module Validations
5
+ extend ActiveSupport::Concern
6
+ include Neo4j::Shared::Validations
7
+
8
+
9
+ # @return [Boolean] true if valid
10
+ def valid?(context = nil)
11
+ context ||= (new_record? ? :create : :update)
12
+ super(context)
13
+ errors.empty?
14
+ end
15
+
16
+ module ClassMethods
17
+ def validates_uniqueness_of(*attr_names)
18
+ validates_with UniquenessValidator, _merge_attributes(attr_names)
19
+ end
20
+ end
21
+
22
+
23
+ class UniquenessValidator < ::ActiveModel::EachValidator
24
+ def initialize(options)
25
+ super(options.reverse_merge(case_sensitive: true))
26
+ end
27
+
28
+ def validate_each(record, attribute, value)
29
+ return unless found(record, attribute, value).exists?
30
+
31
+ record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(value: value))
32
+ end
33
+
34
+ def found(record, attribute, value)
35
+ conditions = scope_conditions(record)
36
+
37
+ # TODO: Added as find(:name => nil) throws error
38
+ value = '' if value.nil?
39
+
40
+ conditions[attribute] = options[:case_sensitive] ? value : /#{Regexp.escape(value.to_s)}/i
41
+
42
+ found = record.class.as(:result).where(conditions)
43
+ found = found.where_not(neo_id: record.neo_id) if record._persisted_obj
44
+ found
45
+ end
46
+
47
+ def message(instance)
48
+ super || 'has already been taken'
49
+ end
50
+
51
+ def scope_conditions(instance)
52
+ Array(options[:scope] || []).inject({}) do |conditions, key|
53
+ conditions.merge(key => instance[key])
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,67 @@
1
+ module Neo4j
2
+ # Makes Neo4j Relationships more or less act like ActiveRecord objects.
3
+ # See documentation at https://github.com/neo4jrb/neo4j/wiki/Neo4j%3A%3AActiveRel
4
+ module ActiveRel
5
+ extend ActiveSupport::Concern
6
+
7
+ MARSHAL_INSTANCE_VARIABLES = [:@attributes, :@rel_type, :@_persisted_obj]
8
+
9
+ include Neo4j::Shared
10
+ include Neo4j::ActiveRel::Initialize
11
+ include Neo4j::Shared::Identity
12
+ include Neo4j::Shared::Marshal
13
+ include Neo4j::Shared::SerializedProperties
14
+ include Neo4j::ActiveRel::Property
15
+ include Neo4j::ActiveRel::Persistence
16
+ include Neo4j::ActiveRel::Validations
17
+ include Neo4j::ActiveRel::Callbacks
18
+ include Neo4j::ActiveRel::Query
19
+ include Neo4j::ActiveRel::Types
20
+ include Neo4j::Shared::Enum
21
+ include Neo4j::Shared::PermittedAttributes
22
+
23
+ class FrozenRelError < Neo4j::Error; end
24
+
25
+ def initialize(from_node = nil, to_node = nil, args = nil)
26
+ load_nodes(node_or_nil(from_node), node_or_nil(to_node))
27
+ resolved_args = hash_or_nil(from_node, args)
28
+ symbol_args = sanitize_input_parameters(resolved_args)
29
+ super(symbol_args)
30
+ end
31
+
32
+ def node_cypher_representation(node)
33
+ node_class = node.class
34
+ id_name = node_class.id_property_name
35
+ labels = ':' + node_class.mapped_label_names.join(':')
36
+
37
+ "(#{labels} {#{id_name}: #{node.id.inspect}})"
38
+ end
39
+
40
+ def neo4j_obj
41
+ _persisted_obj || fail('Tried to access native neo4j object on a non persisted object')
42
+ end
43
+
44
+ included do
45
+ include Neo4j::Timestamps if Neo4j::Config[:record_timestamps]
46
+
47
+ def self.inherited(other)
48
+ attributes.each_pair do |k, v|
49
+ other.inherit_property k.to_sym, v.clone, declared_properties[k].options
50
+ end
51
+ super
52
+ end
53
+ end
54
+
55
+ ActiveSupport.run_load_hooks(:active_rel, self)
56
+
57
+ private
58
+
59
+ def node_or_nil(node)
60
+ node.is_a?(Neo4j::ActiveNode) || node.is_a?(Integer) ? node : nil
61
+ end
62
+
63
+ def hash_or_nil(node_or_hash, hash_or_nil)
64
+ hash_or_parameter?(node_or_hash) ? node_or_hash : hash_or_nil
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,15 @@
1
+ module Neo4j
2
+ module ActiveRel
3
+ module Callbacks #:nodoc:
4
+ extend ActiveSupport::Concern
5
+ include Neo4j::Shared::Callbacks
6
+
7
+ def save(*args)
8
+ unless _persisted_obj || (from_node.respond_to?(:neo_id) && to_node.respond_to?(:neo_id))
9
+ fail Neo4j::ActiveRel::Persistence::RelInvalidError, 'from_node and to_node must be node objects'
10
+ end
11
+ super(*args)
12
+ end
13
+ end
14
+ end
15
+ end