neo4j_legacy 7.2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1357 -0
  3. data/CONTRIBUTORS +8 -0
  4. data/Gemfile +38 -0
  5. data/README.md +103 -0
  6. data/bin/neo4j-jars +33 -0
  7. data/bin/rake +17 -0
  8. data/config/locales/en.yml +5 -0
  9. data/config/neo4j/add_classnames.yml +1 -0
  10. data/config/neo4j/config.yml +35 -0
  11. data/lib/active_support/per_thread_registry.rb +1 -0
  12. data/lib/backports/action_controller/metal/strong_parameters.rb +672 -0
  13. data/lib/backports/active_model/forbidden_attributes_protection.rb +30 -0
  14. data/lib/backports/active_support/concern.rb +13 -0
  15. data/lib/backports/active_support/core_ext/module/attribute_accessors.rb +10 -0
  16. data/lib/backports/active_support/logger.rb +99 -0
  17. data/lib/backports/active_support/logger_silence.rb +27 -0
  18. data/lib/backports/active_support/logger_thread_safe_level.rb +32 -0
  19. data/lib/backports/active_support/per_thread_registry.rb +60 -0
  20. data/lib/backports.rb +4 -0
  21. data/lib/neo4j/active_node/callbacks.rb +8 -0
  22. data/lib/neo4j/active_node/dependent/association_methods.rb +48 -0
  23. data/lib/neo4j/active_node/dependent/query_proxy_methods.rb +50 -0
  24. data/lib/neo4j/active_node/dependent.rb +11 -0
  25. data/lib/neo4j/active_node/enum.rb +29 -0
  26. data/lib/neo4j/active_node/has_n/association/rel_factory.rb +61 -0
  27. data/lib/neo4j/active_node/has_n/association/rel_wrapper.rb +23 -0
  28. data/lib/neo4j/active_node/has_n/association.rb +280 -0
  29. data/lib/neo4j/active_node/has_n/association_cypher_methods.rb +108 -0
  30. data/lib/neo4j/active_node/has_n.rb +532 -0
  31. data/lib/neo4j/active_node/id_property/accessor.rb +62 -0
  32. data/lib/neo4j/active_node/id_property.rb +187 -0
  33. data/lib/neo4j/active_node/initialize.rb +21 -0
  34. data/lib/neo4j/active_node/labels/index.rb +87 -0
  35. data/lib/neo4j/active_node/labels/reloading.rb +21 -0
  36. data/lib/neo4j/active_node/labels.rb +198 -0
  37. data/lib/neo4j/active_node/node_wrapper.rb +52 -0
  38. data/lib/neo4j/active_node/orm_adapter.rb +82 -0
  39. data/lib/neo4j/active_node/persistence.rb +175 -0
  40. data/lib/neo4j/active_node/property.rb +60 -0
  41. data/lib/neo4j/active_node/query/query_proxy.rb +361 -0
  42. data/lib/neo4j/active_node/query/query_proxy_eager_loading.rb +61 -0
  43. data/lib/neo4j/active_node/query/query_proxy_enumerable.rb +90 -0
  44. data/lib/neo4j/active_node/query/query_proxy_find_in_batches.rb +19 -0
  45. data/lib/neo4j/active_node/query/query_proxy_link.rb +117 -0
  46. data/lib/neo4j/active_node/query/query_proxy_methods.rb +210 -0
  47. data/lib/neo4j/active_node/query/query_proxy_methods_of_mass_updating.rb +83 -0
  48. data/lib/neo4j/active_node/query.rb +76 -0
  49. data/lib/neo4j/active_node/query_methods.rb +65 -0
  50. data/lib/neo4j/active_node/reflection.rb +86 -0
  51. data/lib/neo4j/active_node/rels.rb +11 -0
  52. data/lib/neo4j/active_node/scope.rb +146 -0
  53. data/lib/neo4j/active_node/unpersisted.rb +48 -0
  54. data/lib/neo4j/active_node/validations.rb +59 -0
  55. data/lib/neo4j/active_node.rb +105 -0
  56. data/lib/neo4j/active_rel/callbacks.rb +15 -0
  57. data/lib/neo4j/active_rel/initialize.rb +28 -0
  58. data/lib/neo4j/active_rel/persistence/query_factory.rb +95 -0
  59. data/lib/neo4j/active_rel/persistence.rb +114 -0
  60. data/lib/neo4j/active_rel/property.rb +95 -0
  61. data/lib/neo4j/active_rel/query.rb +95 -0
  62. data/lib/neo4j/active_rel/rel_wrapper.rb +22 -0
  63. data/lib/neo4j/active_rel/related_node.rb +83 -0
  64. data/lib/neo4j/active_rel/types.rb +82 -0
  65. data/lib/neo4j/active_rel/validations.rb +8 -0
  66. data/lib/neo4j/active_rel.rb +67 -0
  67. data/lib/neo4j/class_arguments.rb +39 -0
  68. data/lib/neo4j/config.rb +124 -0
  69. data/lib/neo4j/core/query.rb +22 -0
  70. data/lib/neo4j/errors.rb +28 -0
  71. data/lib/neo4j/migration.rb +127 -0
  72. data/lib/neo4j/paginated.rb +27 -0
  73. data/lib/neo4j/railtie.rb +169 -0
  74. data/lib/neo4j/schema/operation.rb +91 -0
  75. data/lib/neo4j/shared/attributes.rb +220 -0
  76. data/lib/neo4j/shared/callbacks.rb +64 -0
  77. data/lib/neo4j/shared/cypher.rb +37 -0
  78. data/lib/neo4j/shared/declared_properties.rb +204 -0
  79. data/lib/neo4j/shared/declared_property/index.rb +37 -0
  80. data/lib/neo4j/shared/declared_property.rb +118 -0
  81. data/lib/neo4j/shared/enum.rb +148 -0
  82. data/lib/neo4j/shared/filtered_hash.rb +79 -0
  83. data/lib/neo4j/shared/identity.rb +28 -0
  84. data/lib/neo4j/shared/initialize.rb +28 -0
  85. data/lib/neo4j/shared/marshal.rb +23 -0
  86. data/lib/neo4j/shared/mass_assignment.rb +58 -0
  87. data/lib/neo4j/shared/permitted_attributes.rb +28 -0
  88. data/lib/neo4j/shared/persistence.rb +231 -0
  89. data/lib/neo4j/shared/property.rb +220 -0
  90. data/lib/neo4j/shared/query_factory.rb +101 -0
  91. data/lib/neo4j/shared/rel_type_converters.rb +43 -0
  92. data/lib/neo4j/shared/serialized_properties.rb +30 -0
  93. data/lib/neo4j/shared/type_converters.rb +418 -0
  94. data/lib/neo4j/shared/typecasted_attributes.rb +98 -0
  95. data/lib/neo4j/shared/typecaster.rb +53 -0
  96. data/lib/neo4j/shared/validations.rb +48 -0
  97. data/lib/neo4j/shared.rb +51 -0
  98. data/lib/neo4j/tasks/migration.rake +24 -0
  99. data/lib/neo4j/timestamps/created.rb +9 -0
  100. data/lib/neo4j/timestamps/updated.rb +9 -0
  101. data/lib/neo4j/timestamps.rb +11 -0
  102. data/lib/neo4j/type_converters.rb +7 -0
  103. data/lib/neo4j/version.rb +3 -0
  104. data/lib/neo4j/wrapper.rb +4 -0
  105. data/lib/neo4j.rb +96 -0
  106. data/lib/rails/generators/neo4j/model/model_generator.rb +86 -0
  107. data/lib/rails/generators/neo4j/model/templates/model.erb +15 -0
  108. data/lib/rails/generators/neo4j_generator.rb +67 -0
  109. data/neo4j.gemspec +43 -0
  110. metadata +389 -0
@@ -0,0 +1,114 @@
1
+ module Neo4j::ActiveRel
2
+ module Persistence
3
+ extend ActiveSupport::Concern
4
+ include Neo4j::Shared::Cypher::RelIdentifiers
5
+ include Neo4j::Shared::Persistence
6
+
7
+ class RelInvalidError < RuntimeError; end
8
+ class ModelClassInvalidError < RuntimeError; end
9
+ class RelCreateFailedError < RuntimeError; end
10
+
11
+ def from_node_identifier
12
+ @from_node_identifier || :from_node
13
+ end
14
+
15
+ def to_node_identifier
16
+ @to_node_identifier || :to_node
17
+ end
18
+
19
+ def from_node_identifier=(id)
20
+ @from_node_identifier = id.to_sym
21
+ end
22
+
23
+ def to_node_identifier=(id)
24
+ @to_node_identifier = id.to_sym
25
+ end
26
+
27
+ def cypher_identifier
28
+ @cypher_identifier || :rel
29
+ end
30
+
31
+ def save(*)
32
+ create_or_update
33
+ end
34
+
35
+ def save!(*args)
36
+ save(*args) or fail(RelInvalidError, inspect) # rubocop:disable Style/AndOr
37
+ end
38
+
39
+ # Increments concurrently a numeric attribute by a centain amount
40
+ # @param [Symbol, String] name of the attribute to increment
41
+ # @param [Integer, Float] amount to increment
42
+ def concurrent_increment!(attribute, by = 1)
43
+ query_rel = Neo4j::Session.query.match('()-[n]-()').where(n: {neo_id: neo_id})
44
+ increment_by_query! query_rel, attribute, by
45
+ end
46
+
47
+ def create_model
48
+ validate_node_classes!
49
+ rel = _create_rel
50
+ return self unless rel.respond_to?(:props)
51
+ init_on_load(rel, from_node, to_node, @rel_type)
52
+ true
53
+ end
54
+
55
+ module ClassMethods
56
+ # Creates a new relationship between objects
57
+ # @param [Hash] props the properties the new relationship should have
58
+ def create(*args)
59
+ new(*args).tap(&:save)
60
+ end
61
+
62
+ # Same as #create, but raises an error if there is a problem during save.
63
+ def create!(*args)
64
+ new(*args).tap(&:save!)
65
+ end
66
+
67
+ def create_method
68
+ creates_unique? ? :create_unique : :create
69
+ end
70
+
71
+ def load_entity(id)
72
+ Neo4j::Relationship.load(id)
73
+ end
74
+ end
75
+
76
+ def create_method
77
+ self.class.create_method
78
+ end
79
+
80
+ private
81
+
82
+ def validate_node_classes!
83
+ [from_node, to_node].each do |node|
84
+ type = from_node == node ? :_from_class : :_to_class
85
+ type_class = self.class.send(type)
86
+
87
+ unless valid_type?(type_class, node)
88
+ fail ModelClassInvalidError, type_validation_error_message(node, type_class)
89
+ end
90
+ end
91
+ end
92
+
93
+ def valid_type?(type_object, node)
94
+ case type_object
95
+ when false, :any
96
+ true
97
+ when Array
98
+ type_object.any? { |c| valid_type?(c, node) }
99
+ else
100
+ node.class.mapped_label_names.include?(type_object.to_s.constantize.mapped_label_name)
101
+ end
102
+ end
103
+
104
+ def type_validation_error_message(node, type_class)
105
+ "Node class was #{node.class} (#{node.class.object_id}), expected #{type_class} (#{type_class.object_id})"
106
+ end
107
+
108
+ def _create_rel
109
+ factory = QueryFactory.new(from_node, to_node, self)
110
+ factory.build!
111
+ factory.unwrapped_rel
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,95 @@
1
+ require 'neo4j/class_arguments'
2
+
3
+ module Neo4j::ActiveRel
4
+ module Property
5
+ extend ActiveSupport::Concern
6
+ include Neo4j::Shared::Property
7
+
8
+ %w(to_node from_node).each do |direction|
9
+ define_method("#{direction}") { instance_variable_get("@#{direction}") }
10
+ define_method("#{direction}=") do |argument|
11
+ fail FrozenRelError, 'Relationship start/end nodes cannot be changed once persisted' if _persisted_obj
12
+ instance_variable_set("@#{direction}", argument)
13
+ end
14
+ end
15
+
16
+ alias_method :start_node, :from_node
17
+ alias_method :end_node, :to_node
18
+
19
+ %w(start_node end_node).each do |direction|
20
+ define_method("#{direction}_neo_id") { send(direction).neo_id if direction }
21
+ end
22
+ alias_method :from_node_neo_id, :start_node_neo_id
23
+ alias_method :to_node_neo_id, :end_node_neo_id
24
+
25
+ # @return [String] a string representing the relationship type that will be created
26
+ def type
27
+ self.class.type
28
+ end
29
+ alias_method :rel_type, :type
30
+
31
+ def initialize(attributes = nil)
32
+ super(attributes)
33
+ end
34
+
35
+ def creates_unique_option
36
+ self.class.creates_unique_option
37
+ end
38
+
39
+ module ClassMethods
40
+ include Neo4j::Shared::Cypher::CreateMethod
41
+
42
+ # Extracts keys from attributes hash which are relationships of the model
43
+ # TODO: Validate separately that relationships are getting the right values? Perhaps also store the values and persist relationships on save?
44
+ def extract_association_attributes!(attributes)
45
+ return if attributes.blank?
46
+ {}.tap do |relationship_props|
47
+ attributes.each_key do |key|
48
+ relationship_props[key] = attributes.delete(key) if [:from_node, :to_node].include?(key)
49
+ end
50
+ end
51
+ end
52
+
53
+ def id_property_name
54
+ false
55
+ end
56
+
57
+ %w(to_class from_class).each do |direction|
58
+ define_method("#{direction}") do |argument = nil|
59
+ if !argument.nil?
60
+ Neo4j::ClassArguments.validate_argument!(argument, direction)
61
+
62
+ instance_variable_set("@#{direction}", argument)
63
+ end
64
+
65
+ self.instance_variable_get("@#{direction}")
66
+ end
67
+
68
+ define_method("_#{direction}") { instance_variable_get "@#{direction}" }
69
+ end
70
+
71
+ def valid_class_argument?(class_argument)
72
+ [String, Symbol, FalseClass].include?(class_argument.class) ||
73
+ (class_argument.is_a?(Array) && class_argument.all? { |c| [String, Symbol].include?(c.class) })
74
+ end
75
+
76
+ alias_method :start_class, :from_class
77
+ alias_method :end_class, :to_class
78
+
79
+ def load_entity(id)
80
+ Neo4j::Node.load(id)
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ def load_nodes(from_node = nil, to_node = nil)
87
+ @from_node = RelatedNode.new(from_node)
88
+ @to_node = RelatedNode.new(to_node)
89
+ end
90
+
91
+ def inspect_attributes
92
+ attributes.to_a
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,95 @@
1
+ module Neo4j::ActiveRel
2
+ module Query
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ # Returns the object with the specified neo4j id.
7
+ # @param [String,Integer] id of node to find
8
+ # @param [Neo4j::Session] session optional
9
+ def find(id, session = self.neo4j_session)
10
+ fail "Unknown argument #{id.class} in find method (expected String or Integer)" if !(id.is_a?(String) || id.is_a?(Integer))
11
+ find_by_id(id, session)
12
+ end
13
+
14
+ # Loads the relationship using its neo_id.
15
+ def find_by_id(key, session = Neo4j::Session.current!)
16
+ session.query.match('()-[r]-()').where('ID(r)' => key.to_i).limit(1).return(:r).first.r
17
+ end
18
+
19
+ # Performs a very basic match on the relationship.
20
+ # This is not executed lazily, it will immediately return matching objects.
21
+ # To use a string, prefix the property with "r1"
22
+ # @example Match with a string
23
+ # MyRelClass.where('r1.grade > r1')
24
+ def where(args = {})
25
+ where_query.where(where_string(args)).pluck(:r1)
26
+ end
27
+
28
+ # Performs a basic match on the relationship, returning all results.
29
+ # This is not executed lazily, it will immediately return matching objects.
30
+ def all
31
+ all_query.pluck(:r1)
32
+ end
33
+
34
+ def first
35
+ all_query.limit(1).order('ID(r1)').pluck(:r1).first
36
+ end
37
+
38
+ def last
39
+ all_query.limit(1).order('ID(r1) DESC').pluck(:r1).first
40
+ end
41
+
42
+ private
43
+
44
+ def deprecation_warning!
45
+ ActiveSupport::Deprecation.warn 'The Neo4j::ActiveRel::Query module has been deprecated and will be removed in a future version of the gem.', caller
46
+ end
47
+
48
+ def where_query
49
+ deprecation_warning!
50
+ Neo4j::Session.query.match("#{cypher_string(:outbound)}-[r1:`#{self._type}`]->#{cypher_string(:inbound)}")
51
+ end
52
+
53
+ def all_query
54
+ deprecation_warning!
55
+ Neo4j::Session.query.match("#{cypher_string}-[r1:`#{self._type}`]->#{cypher_string(:inbound)}")
56
+ end
57
+
58
+ def cypher_string(dir = :outbound)
59
+ case dir
60
+ when :outbound
61
+ identifier = '(n1'
62
+ identifier + (_from_class == :any ? ')' : cypher_label(:outbound))
63
+ when :inbound
64
+ identifier = '(n2'
65
+ identifier + (_to_class == :any ? ')' : cypher_label(:inbound))
66
+ end
67
+ end
68
+
69
+ def cypher_label(dir = :outbound)
70
+ target_class = dir == :outbound ? as_constant(_from_class) : as_constant(_to_class)
71
+ ":`#{target_class.mapped_label_name}`)"
72
+ end
73
+
74
+ def as_constant(given_class)
75
+ case given_class
76
+ when String, Symbol
77
+ given_class.to_s.constantize
78
+ when Array
79
+ fail "ActiveRel query methods are being deprecated and do not support Array (from|to)_class options. Current value: #{given_class}"
80
+ else
81
+ given_class
82
+ end
83
+ end
84
+
85
+ def where_string(args)
86
+ case args
87
+ when Hash
88
+ args.map { |k, v| v.is_a?(Integer) ? "r1.#{k} = #{v}" : "r1.#{k} = '#{v}'" }.join(', ')
89
+ else
90
+ args
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,22 @@
1
+ class Neo4j::Relationship
2
+ module Wrapper
3
+ def wrapper
4
+ props.symbolize_keys!
5
+ begin
6
+ most_concrete_class = class_from_type
7
+ wrapped_rel = most_concrete_class.constantize.new
8
+ rescue NameError
9
+ return self
10
+ end
11
+
12
+ wrapped_rel.init_on_load(self, self._start_node_id, self._end_node_id, self.rel_type)
13
+ wrapped_rel
14
+ end
15
+
16
+ private
17
+
18
+ def class_from_type
19
+ Neo4j::ActiveRel::Types::WRAPPED_CLASSES[rel_type] || Neo4j::ActiveRel::Types::WRAPPED_CLASSES[rel_type] = rel_type.camelize
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,83 @@
1
+ module Neo4j::ActiveRel
2
+ # A container for ActiveRel's :inbound and :outbound methods. It provides lazy loading of nodes.
3
+ # It's important (or maybe not really IMPORTANT, but at least worth mentioning) that calling method_missing
4
+ # will result in a query to load the node if the node is not already loaded.
5
+ class RelatedNode
6
+ class UnsetRelatedNodeError < Neo4j::Error; end
7
+
8
+ # ActiveRel's related nodes can be initialized with nothing, an integer, or a fully wrapped node.
9
+ #
10
+ # Initialization with nothing happens when a new, non-persisted ActiveRel object is first initialized.
11
+ #
12
+ # Initialization with an integer happens when a relationship is loaded from the database. It loads using the ID
13
+ # because that is provided by the Cypher response and does not require an extra query.
14
+ def initialize(node = nil)
15
+ @node = valid_node_param?(node) ? node : (fail Neo4j::InvalidParameterError, 'RelatedNode must be initialized with either a node ID or node')
16
+ end
17
+
18
+ # Loads the node if needed, then conducts comparison.
19
+ def ==(other)
20
+ loaded if @node.is_a?(Integer)
21
+ @node == other
22
+ end
23
+
24
+ # Returns the neo_id of a given node without loading.
25
+ def neo_id
26
+ loaded? ? @node.neo_id : @node
27
+ end
28
+
29
+ # Loads a node from the database or returns the node if already laoded
30
+ def loaded
31
+ fail UnsetRelatedNodeError, 'Node not set, cannot load' if @node.nil?
32
+ @node = @node.respond_to?(:neo_id) ? @node : Neo4j::Node.load(@node)
33
+ end
34
+
35
+ # @param [String, Symbol, Array] clazz An alternate label to use in the event the node is not present or loaded
36
+ def cypher_representation(clazz)
37
+ case
38
+ when !set?
39
+ "(#{formatted_label_list(clazz)})"
40
+ when set? && !loaded?
41
+ "(Node with neo_id #{@node})"
42
+ else
43
+ node_class = self.class
44
+ id_name = node_class.id_property_name
45
+ labels = ':' + node_class.mapped_label_names.join(':')
46
+
47
+ "(#{labels} {#{id_name}: #{@node.id.inspect}})"
48
+ end
49
+ end
50
+
51
+ # @return [Boolean] indicates whether a node has or has not been fully loaded from the database
52
+ def loaded?
53
+ @node.respond_to?(:neo_id)
54
+ end
55
+
56
+ def set?
57
+ !@node.nil?
58
+ end
59
+
60
+ def method_missing(*args, &block)
61
+ loaded.send(*args, &block)
62
+ end
63
+
64
+ def respond_to_missing?(method_name, include_private = false)
65
+ loaded if @node.is_a?(Numeric)
66
+ @node.respond_to?(method_name) ? true : super
67
+ end
68
+
69
+ def class
70
+ loaded.send(:class)
71
+ end
72
+
73
+ private
74
+
75
+ def formatted_label_list(list)
76
+ list.is_a?(Array) ? list.join(' || ') : list
77
+ end
78
+
79
+ def valid_node_param?(node)
80
+ node.nil? || node.is_a?(Integer) || node.respond_to?(:neo_id)
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,82 @@
1
+ module Neo4j
2
+ module ActiveRel
3
+ # provides mapping of type to model name
4
+ module Types
5
+ extend ActiveSupport::Concern
6
+
7
+ # WRAPPED_CLASSES maps relationship types to ActiveRel models.
8
+ #
9
+ # Typically, it's a 1:1 relationship, with a type having a model of the same name. Sometimes, someone needs to be a precious
10
+ # snowflake and have a model name that doesn't match the rel type, so this comes in handy.
11
+ #
12
+ # As an example, Chris often finds it easier to name models after the classes that use the relationship: `StudentLesson` instead of
13
+ # `EnrolledIn`, because it's easier to remember "A student has a relationship to lesson" than "the type of relationship between Student
14
+ # and Lesson is 'EnrolledIn'." After all, that is a big part of why we have models, right? To make our lives easier?
15
+ #
16
+ # A model is added to WRAPPED_CLASSES when it is initalized AND when the `type` class method is called within a model. This means that
17
+ # it's possible a model will be added twice: once with the rel_type version of the model name, again with the custom type. deal_with_it.gif.
18
+ WRAPPED_CLASSES = {}
19
+
20
+ included do
21
+ type self.namespaced_model_name, true
22
+ end
23
+
24
+ module ClassMethods
25
+ include Neo4j::Shared::RelTypeConverters
26
+
27
+ def inherited(subclass)
28
+ subclass.type subclass.namespaced_model_name, true
29
+ end
30
+
31
+ # When called without arguments, it will return the current setting or supply a default.
32
+ # When called with arguments, it will change the current setting.
33
+ # @param [String] given_type sets the relationship type when creating relationships via this class
34
+ # @param [Boolean] auto Should the given_type be changed in compliance with the gem's rel decorator setting?
35
+ def type(given_type = nil, auto = false)
36
+ case
37
+ when !given_type && rel_type?
38
+ @rel_type
39
+ when given_type
40
+ assign_type!(given_type, auto)
41
+ else
42
+ assign_type!(namespaced_model_name, true)
43
+ end
44
+ end
45
+ alias_method :rel_type, :type
46
+ alias_method :_type, :type # should be deprecated
47
+
48
+ def namespaced_model_name
49
+ case Neo4j::Config[:module_handling]
50
+ when :demodulize
51
+ self.name.demodulize
52
+ when Proc
53
+ Neo4j::Config[:module_handling].call(self.name)
54
+ else
55
+ self.name
56
+ end
57
+ end
58
+
59
+ def _wrapped_classes
60
+ WRAPPED_CLASSES
61
+ end
62
+
63
+ def add_wrapped_class(type)
64
+ # WRAPPED_CLASSES[type.to_sym.downcase] = self.name
65
+ _wrapped_classes[type.to_sym] = self.name
66
+ end
67
+
68
+ def rel_type?
69
+ !!@rel_type
70
+ end
71
+
72
+ private
73
+
74
+ def assign_type!(given_type, auto)
75
+ @rel_type = (auto ? decorated_rel_type(given_type) : given_type).tap do |type|
76
+ add_wrapped_class(type)
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,8 @@
1
+ module Neo4j
2
+ module ActiveRel
3
+ module Validations
4
+ extend ActiveSupport::Concern
5
+ include Neo4j::Shared::Validations
6
+ end
7
+ end
8
+ 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,39 @@
1
+ module Neo4j
2
+ module ClassArguments
3
+ class << self
4
+ INVALID_CLASS_ARGUMENT_ERROR = 'option must by String, Symbol, false, nil, or an Array of Symbols/Strings'
5
+
6
+ def valid_argument?(class_argument)
7
+ [NilClass, String, Symbol, FalseClass].include?(class_argument.class) ||
8
+ (class_argument.is_a?(Array) && class_argument.all? { |c| [Symbol, String].include?(c.class) })
9
+ end
10
+
11
+ def validate_argument!(class_argument, context)
12
+ return if valid_argument?(class_argument)
13
+
14
+ fail ArgumentError, "#{context} #{INVALID_CLASS_ARGUMENT_ERROR} (was #{class_argument.inspect})"
15
+ end
16
+
17
+ def active_node_model?(class_constant)
18
+ class_constant.included_modules.include?(Neo4j::ActiveNode)
19
+ end
20
+
21
+ def constantize_argument(class_argument)
22
+ case class_argument
23
+ when 'any', :any, false, nil
24
+ nil
25
+ when Array
26
+ class_argument.map(&method(:constantize_argument))
27
+ else
28
+ class_argument.to_s.constantize.tap do |class_constant|
29
+ if !active_node_model?(class_constant)
30
+ fail ArgumentError, "#{class_constant} is not an ActiveNode model"
31
+ end
32
+ end
33
+ end
34
+ rescue NameError
35
+ raise ArgumentError, "Could not find class: #{class_argument}"
36
+ end
37
+ end
38
+ end
39
+ end