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,220 @@
1
+ module Neo4j::Shared
2
+ # Attributes provides a set of class methods for defining an attributes
3
+ # schema and instance methods for reading and writing attributes.
4
+ #
5
+ # @example Usage
6
+ # class Person
7
+ # include Neo4j::Shared::Attributes
8
+ # attribute :name
9
+ # end
10
+ #
11
+ # person = Person.new
12
+ # person.name = "Ben Poweski"
13
+ #
14
+ # Originally part of ActiveAttr, https://github.com/cgriego/active_attr
15
+ module Attributes
16
+ extend ActiveSupport::Concern
17
+ include ActiveModel::AttributeMethods
18
+
19
+ # Methods deprecated on the Object class which can be safely overridden
20
+ DEPRECATED_OBJECT_METHODS = %w(id type)
21
+
22
+ included do
23
+ attribute_method_suffix '' if attribute_method_matchers.none? { |matcher| matcher.prefix == '' && matcher.suffix == '' }
24
+ attribute_method_suffix '='
25
+ attribute_method_suffix '?'
26
+ end
27
+
28
+ # Performs equality checking on the result of attributes and its type.
29
+ #
30
+ # @example Compare for equality.
31
+ # model == other
32
+ #
33
+ # @param [ActiveAttr::Attributes, Object] other The other model to compare
34
+ #
35
+ # @return [true, false] True if attributes are equal and other is instance
36
+ # of the same Class, false if not.
37
+ def ==(other)
38
+ return false unless other.instance_of? self.class
39
+ attributes == other.attributes
40
+ end
41
+
42
+ # Returns a Hash of all attributes
43
+ #
44
+ # @example Get attributes
45
+ # person.attributes # => {"name"=>"Ben Poweski"}
46
+ #
47
+ # @return [Hash{String => Object}] The Hash of all attributes
48
+ def attributes
49
+ attributes_map { |name| send name }
50
+ end
51
+
52
+ # Write a single attribute to the model's attribute hash.
53
+ #
54
+ # @example Write the attribute with write_attribute
55
+ # person.write_attribute(:name, "Benjamin")
56
+ # @example Write an attribute with bracket syntax
57
+ # person[:name] = "Benjamin"
58
+ #
59
+ # @param [String, Symbol, #to_s] name The name of the attribute to update.
60
+ # @param [Object] value The value to set for the attribute.
61
+ #
62
+ # @raise [UnknownAttributeError] if the attribute is unknown
63
+ def write_attribute(name, value)
64
+ if respond_to? "#{name}="
65
+ send "#{name}=", value
66
+ else
67
+ fail Neo4j::UnknownAttributeError, "unknown attribute: #{name}"
68
+ end
69
+ end
70
+ alias_method :[]=, :write_attribute
71
+
72
+ def query_attribute(name)
73
+ if respond_to? "#{name}?"
74
+ send "#{name}?"
75
+ else
76
+ fail Neo4j::UnknownAttributeError, "unknown attribute: #{name}"
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ # Read an attribute from the attributes hash
83
+ def attribute(name)
84
+ @attributes ||= {}
85
+ @attributes[name]
86
+ end
87
+
88
+ # Write an attribute to the attributes hash
89
+ def attribute=(name, value)
90
+ @attributes ||= {}
91
+ @attributes[name] = value
92
+ end
93
+
94
+ # Maps all attributes using the given block
95
+ #
96
+ # @example Stringify attributes
97
+ # person.attributes_map { |name| send(name).to_s }
98
+ #
99
+ # @yield [name] block called to return hash value
100
+ # @yieldparam [String] name The name of the attribute to map.
101
+ #
102
+ # @return [Hash{String => Object}] The Hash of mapped attributes
103
+ def attributes_map
104
+ Hash[self.class.attribute_names.map { |name| [name, yield(name)] }]
105
+ end
106
+
107
+ def attribute?(name)
108
+ Neo4j::Shared::TypeConverters::BooleanConverter.to_ruby(read_attribute(name))
109
+ end
110
+
111
+ module ClassMethods
112
+ # Defines an attribute
113
+ #
114
+ # For each attribute that is defined, a getter and setter will be
115
+ # added as an instance method to the model. An
116
+ # {AttributeDefinition} instance will be added to result of the
117
+ # attributes class method.
118
+ #
119
+ # @example Define an attribute.
120
+ # attribute :name
121
+ #
122
+ # @param (see AttributeDefinition#initialize)
123
+ #
124
+ # @raise [DangerousAttributeError] if the attribute name conflicts with
125
+ # existing methods
126
+ #
127
+ # @return [AttributeDefinition] Attribute's definition
128
+ def attribute(name)
129
+ if dangerous_attribute?(name)
130
+ fail Neo4j::DangerousAttributeError, %(an attribute method named "#{name}" would conflict with an existing method)
131
+ else
132
+ attribute!(name)
133
+ end
134
+ end
135
+
136
+ # Returns an Array of attribute names as Strings
137
+ #
138
+ # @example Get attribute names
139
+ # Person.attribute_names
140
+ #
141
+ # @return [Array<String>] The attribute names
142
+ def attribute_names
143
+ attributes.keys
144
+ end
145
+
146
+ # Returns a Hash of AttributeDefinition instances
147
+ #
148
+ # @example Get attribute definitions
149
+ # Person.attributes
150
+ #
151
+ # @return [ActiveSupport::HashWithIndifferentAccess{String => Neo4j::Shared::AttributeDefinition}]
152
+ # The Hash of AttributeDefinition instances
153
+ def attributes
154
+ @attributes ||= ActiveSupport::HashWithIndifferentAccess.new
155
+ end
156
+
157
+ # Determine if a given attribute name is dangerous
158
+ #
159
+ # Some attribute names can cause conflicts with existing methods
160
+ # on an object. For example, an attribute named "timeout" would
161
+ # conflict with the timeout method that Ruby's Timeout library
162
+ # mixes into Object.
163
+ #
164
+ # @example Testing a harmless attribute
165
+ # Person.dangerous_attribute? :name #=> false
166
+ #
167
+ # @example Testing a dangerous attribute
168
+ # Person.dangerous_attribute? :nil #=> "nil?"
169
+ #
170
+ # @param name Attribute name
171
+ #
172
+ # @return [false, String] False or the conflicting method name
173
+ def dangerous_attribute?(name)
174
+ attribute_methods(name).detect do |method_name|
175
+ !DEPRECATED_OBJECT_METHODS.include?(method_name.to_s) && allocate.respond_to?(method_name, true)
176
+ end unless attribute_names.include? name.to_s
177
+ end
178
+
179
+ # Returns the class name plus its attribute names
180
+ #
181
+ # @example Inspect the model's definition.
182
+ # Person.inspect
183
+ #
184
+ # @return [String] Human-readable presentation of the attributes
185
+ def inspect
186
+ inspected_attributes = attribute_names.sort
187
+ attributes_list = "(#{inspected_attributes.join(', ')})" unless inspected_attributes.empty?
188
+ "#{name}#{attributes_list}"
189
+ end
190
+
191
+ protected
192
+
193
+ # Assign a set of attribute definitions, used when subclassing models
194
+ #
195
+ # @param [Array<Neo4j::Shared::DeclaredProperties>] The Array of
196
+ # AttributeDefinition instances
197
+ def attributes=(attributes)
198
+ @attributes = attributes
199
+ end
200
+
201
+ # Overrides ActiveModel::AttributeMethods to backport 3.2 fix
202
+ def instance_method_already_implemented?(method_name)
203
+ generated_attribute_methods.method_defined?(method_name)
204
+ end
205
+
206
+ private
207
+
208
+ # Expand an attribute name into its generated methods names
209
+ def attribute_methods(name)
210
+ attribute_method_matchers.map { |matcher| matcher.method_name name }
211
+ end
212
+
213
+ # Ruby inherited hook to assign superclass attributes to subclasses
214
+ def inherited(subclass)
215
+ super
216
+ subclass.attributes = attributes.dup
217
+ end
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,64 @@
1
+ module Neo4j
2
+ module Shared
3
+ module Callbacks #:nodoc:
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ include ActiveModel::Callbacks
8
+ end
9
+
10
+ included do
11
+ include ActiveModel::Validations::Callbacks
12
+ # after_find is triggered by the `find` method defined in lib/neo4j/active_node/id_property.rb
13
+ define_model_callbacks :initialize, :find, only: :after
14
+ define_model_callbacks :save, :create, :update, :destroy, :touch
15
+ end
16
+
17
+ def initialize(args = nil)
18
+ run_callbacks(:initialize) { super }
19
+ end
20
+
21
+ def destroy #:nodoc:
22
+ tx = Neo4j::Transaction.new
23
+ run_callbacks(:destroy) { super }
24
+ rescue
25
+ @_deleted = false
26
+ @attributes = @attributes.dup
27
+ tx.mark_failed
28
+ raise
29
+ ensure
30
+ tx.close if tx
31
+ end
32
+
33
+ def touch #:nodoc:
34
+ run_callbacks(:touch) { super }
35
+ end
36
+
37
+ # Allows you to perform a callback if a condition is not satisfied.
38
+ # @param [Symbol] kind The callback type to execute unless the guard is true
39
+ # @param [TrueClass,FalseClass] guard When this value is true, the block is yielded without executing callbacks.
40
+ def conditional_callback(kind, guard)
41
+ return yield if guard
42
+ run_callbacks(kind) { yield }
43
+ end
44
+
45
+ private
46
+
47
+ def create_or_update #:nodoc:
48
+ run_callbacks(:save) { super }
49
+ end
50
+
51
+ def create_model #:nodoc:
52
+ Neo4j::Transaction.run do
53
+ run_callbacks(:create) { super }
54
+ end
55
+ end
56
+
57
+ def update_model(*) #:nodoc:
58
+ Neo4j::Transaction.run do
59
+ run_callbacks(:update) { super }
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,37 @@
1
+ module Neo4j::Shared
2
+ module Cypher
3
+ module CreateMethod
4
+ def create_method
5
+ creates_unique? ? :create_unique : :create
6
+ end
7
+
8
+ def creates_unique(option = :none)
9
+ option = :none if option == true
10
+ @creates_unique = option
11
+ end
12
+
13
+ def creates_unique_option
14
+ @creates_unique || :none
15
+ end
16
+
17
+ def creates_unique?
18
+ !!@creates_unique
19
+ end
20
+ alias_method :unique?, :creates_unique?
21
+ end
22
+
23
+ module RelIdentifiers
24
+ extend ActiveSupport::Concern
25
+
26
+ [:from_node, :to_node, :rel].each do |element|
27
+ define_method("#{element}_identifier") do
28
+ instance_variable_get(:"@#{element}_identifier") || element
29
+ end
30
+
31
+ define_method("#{element}_identifier=") do |id|
32
+ instance_variable_set(:"@#{element}_identifier", id.to_sym)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,204 @@
1
+ module Neo4j::Shared
2
+ # The DeclaredPropertyuManager holds details about objects created as a result of calling the #property
3
+ # class method on a class that includes Neo4j::ActiveNode or Neo4j::ActiveRel. There are many options
4
+ # that are referenced frequently, particularly during load and save, so this provides easy access and
5
+ # a way of separating behavior from the general Active{obj} modules.
6
+ #
7
+ # See Neo4j::Shared::DeclaredProperty for definitions of the property objects themselves.
8
+ class DeclaredProperties
9
+ include Neo4j::Shared::TypeConverters
10
+
11
+ attr_reader :klass
12
+ delegate :each, :each_pair, :each_key, :each_value, to: :registered_properties
13
+
14
+ # Each class that includes Neo4j::ActiveNode or Neo4j::ActiveRel gets one instance of this class.
15
+ # @param [#declared_properties] klass An object that has the #declared_properties method.
16
+ def initialize(klass)
17
+ @klass = klass
18
+ end
19
+
20
+ def [](key)
21
+ registered_properties[key.to_sym]
22
+ end
23
+
24
+ def property?(key)
25
+ registered_properties.key?(key.to_sym)
26
+ end
27
+
28
+ # @param [Neo4j::Shared::DeclaredProperty] property An instance of DeclaredProperty, created as the result of calling
29
+ # #property on an ActiveNode or ActiveRel class. The DeclaredProperty has specifics about the property, but registration
30
+ # makes the management object aware of it. This is necessary for type conversion, defaults, and inclusion in the nil and string hashes.
31
+ def register(property)
32
+ @_attributes_nil_hash = nil
33
+ @_attributes_string_map = nil
34
+ registered_properties[property.name] = property
35
+ register_magic_typecaster(property) if property.magic_typecaster
36
+ declared_property_defaults[property.name] = property.default_value if !property.default_value.nil?
37
+ end
38
+
39
+ def index_or_fail!(key, id_property_name, type = :exact)
40
+ return if key == id_property_name
41
+ fail "Cannot index undeclared property #{key}" unless property?(key)
42
+ registered_properties[key].index!(type)
43
+ end
44
+
45
+ def constraint_or_fail!(key, id_property_name, type = :unique)
46
+ return if key == id_property_name
47
+ fail "Cannot constraint undeclared property #{property}" unless property?(key)
48
+ registered_properties[key].constraint!(type)
49
+ end
50
+
51
+ # The :default option in Neo4j::ActiveNode#property class method allows for setting a default value instead of
52
+ # nil on declared properties. This holds those values.
53
+ def declared_property_defaults
54
+ @_default_property_values ||= {}
55
+ end
56
+
57
+ def registered_properties
58
+ @_registered_properties ||= {}
59
+ end
60
+
61
+ def indexed_properties
62
+ registered_properties.select { |_, p| p.index_or_constraint? }
63
+ end
64
+
65
+ # During object wrap, a hash is needed that contains each declared property with a nil value.
66
+ # The active_attr dependency is capable of providing this but it is expensive and calculated on the fly
67
+ # each time it is called. Rather than rely on that, we build this progressively as properties are registered.
68
+ # When the node or rel is loaded, this is used as a template.
69
+ def attributes_nil_hash
70
+ @_attributes_nil_hash ||= {}.tap do |attr_hash|
71
+ registered_properties.each_pair do |k, prop_obj|
72
+ val = prop_obj.default_value
73
+ attr_hash[k.to_s] = val
74
+ end
75
+ end.freeze
76
+ end
77
+
78
+ # During object wrapping, a props hash is built with string keys but Neo4j-core provides symbols.
79
+ # Rather than a `to_s` or `symbolize_keys` during every load, we build a map of symbol-to-string
80
+ # to speed up the process. This increases memory used by the gem but reduces object allocation and GC, so it is faster
81
+ # in practice.
82
+ def attributes_string_map
83
+ @_attributes_string_map ||= {}.tap do |attr_hash|
84
+ attributes_nil_hash.each_key { |k| attr_hash[k.to_sym] = k }
85
+ end.freeze
86
+ end
87
+
88
+ # @param [Symbol] k A symbol for which the String representation is sought. This might seem silly -- we could just call #to_s --
89
+ # but when this happens many times while loading many objects, it results in a surprisingly significant slowdown.
90
+ # The branching logic handles what happens if a property can't be found.
91
+ # The first option attempts to find it in the existing hash.
92
+ # The second option checks whether the key is the class's id property and, if it is, the string hash is rebuilt with it to prevent
93
+ # future lookups.
94
+ # The third calls `to_s`. This would happen if undeclared properties are found on the object. We could add them to the string map
95
+ # but that would result in unchecked, un-GCed memory consumption. In the event that someone is adding properties dynamically,
96
+ # maybe through user input, this would be bad.
97
+ def string_key(k)
98
+ attributes_string_map[k] || string_map_id_property(k) || k.to_s
99
+ end
100
+
101
+ def unregister(name)
102
+ # might need to be include?(name.to_s)
103
+ fail ArgumentError, "Argument `#{name}` not an attribute" if not registered_properties[name]
104
+ registered_properties.delete(name)
105
+ unregister_magic_typecaster(name)
106
+ unregister_property_default(name)
107
+ end
108
+
109
+ def serialize(name, coder = JSON)
110
+ @serialize ||= {}
111
+ @serialize[name] = coder
112
+ end
113
+
114
+ def serialized_properties=(serialize_hash)
115
+ @serialized_property_keys = nil
116
+ @serialize = serialize_hash.clone
117
+ end
118
+
119
+ def serialized_properties
120
+ @serialize ||= {}
121
+ end
122
+
123
+ def serialized_properties_keys
124
+ @serialized_property_keys ||= serialized_properties.keys
125
+ end
126
+
127
+ def magic_typecast_properties_keys
128
+ @magic_typecast_properties_keys ||= magic_typecast_properties.keys
129
+ end
130
+
131
+ def magic_typecast_properties
132
+ @magic_typecast_properties ||= {}
133
+ end
134
+
135
+ EXCLUDED_TYPES = [Array, Range, Regexp]
136
+ def value_for_where(key, value)
137
+ return value unless prop = registered_properties[key]
138
+ return value_for_db(key, value) if prop.typecaster && prop.typecaster.convert_type == value.class
139
+
140
+ if should_convert_for_where?(key, value)
141
+ value_for_db(key, value)
142
+ else
143
+ value
144
+ end
145
+ end
146
+
147
+ def value_for_db(key, value)
148
+ return value unless registered_properties[key]
149
+ convert_property(key, value, :to_db)
150
+ end
151
+
152
+ def value_for_ruby(key, value)
153
+ return unless registered_properties[key]
154
+ convert_property(key, value, :to_ruby)
155
+ end
156
+
157
+ def inject_defaults!(object, props)
158
+ declared_property_defaults.each_pair do |k, v|
159
+ props[k.to_sym] = v if object.send(k).nil? && props[k.to_sym].nil?
160
+ end
161
+ props
162
+ end
163
+
164
+ protected
165
+
166
+ # Prevents repeated calls to :_attribute_type, which isn't free and never changes.
167
+ def fetch_upstream_primitive(attr)
168
+ registered_properties[attr].type
169
+ end
170
+
171
+ private
172
+
173
+ def should_convert_for_where?(key, value)
174
+ (value.is_a?(Array) && supports_array?(key)) || !EXCLUDED_TYPES.include?(value.class)
175
+ end
176
+
177
+ # @param [Symbol] key An undeclared property value found in the _persisted_obj.props hash.
178
+ # Typically, this is a node's id property, which will not be registered as other properties are.
179
+ # In the future, this should probably be reworked a bit. This class should either not know or care
180
+ # about the id property or it should be in charge of it. In the meantime, this improves
181
+ # node load performance.
182
+ def string_map_id_property(key)
183
+ return unless klass.id_property_name == key
184
+ key.to_s.tap do |string_key|
185
+ @_attributes_string_map = attributes_string_map.dup.tap { |h| h[key] = string_key }.freeze
186
+ end
187
+ end
188
+
189
+ def unregister_magic_typecaster(property)
190
+ magic_typecast_properties.delete(property)
191
+ @magic_typecast_properties_keys = nil
192
+ end
193
+
194
+ def unregister_property_default(property)
195
+ declared_property_defaults.delete(property)
196
+ @_default_property_values = nil
197
+ end
198
+
199
+ def register_magic_typecaster(property)
200
+ magic_typecast_properties[property.name] = property.magic_typecaster
201
+ @magic_typecast_properties_keys = nil
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,37 @@
1
+ module Neo4j::Shared
2
+ class DeclaredProperty
3
+ # None of these methods interact with the database. They only keep track of property settings in models.
4
+ # It could (should?) handle the actual indexing/constraining, but that's TBD.
5
+ module Index
6
+ def index_or_constraint?
7
+ index?(:exact) || constraint?(:unique)
8
+ end
9
+
10
+ def index?(type = :exact)
11
+ options.key?(:index) && options[:index] == type
12
+ end
13
+
14
+ def constraint?(type = :unique)
15
+ options.key?(:constraint) && options[:constraint] == type
16
+ end
17
+
18
+ def index!(type = :exact)
19
+ fail Neo4j::InvalidPropertyOptionsError, "Can't set index on constrainted property #{name} (constraints get indexes automatically)" if constraint?(:unique)
20
+ options[:index] = type
21
+ end
22
+
23
+ def constraint!(type = :unique)
24
+ fail Neo4j::InvalidPropertyOptionsError, "Can't set constraint on indexed property #{name} (constraints get indexes automatically)" if index?(:exact)
25
+ options[:constraint] = type
26
+ end
27
+
28
+ def unindex!(type = :exact)
29
+ options.delete(:index) if index?(type)
30
+ end
31
+
32
+ def unconstraint!(type = :unique)
33
+ options.delete(:constraint) if constraint?(type)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,118 @@
1
+ module Neo4j::Shared
2
+ # Contains methods related to the management
3
+ class DeclaredProperty
4
+ include Comparable
5
+
6
+ class IllegalPropertyError < Neo4j::Error; end
7
+ include Neo4j::Shared::DeclaredProperty::Index
8
+
9
+ ILLEGAL_PROPS = %w(from_node to_node start_node end_node)
10
+ attr_reader :name, :name_string, :name_sym, :options, :magic_typecaster
11
+
12
+ def initialize(name, options = {})
13
+ fail IllegalPropertyError, "#{name} is an illegal property" if ILLEGAL_PROPS.include?(name.to_s)
14
+ fail TypeError, "can't convert #{name.class} into Symbol" unless name.respond_to?(:to_sym)
15
+ @name = @name_sym = name.to_sym
16
+ @name_string = name.to_s
17
+ @options = options
18
+ fail_invalid_options!
19
+ end
20
+
21
+ # Compare attribute definitions
22
+ #
23
+ # @example
24
+ # attribute_definition <=> other
25
+ #
26
+ # @param [Neo4j::Shared::DeclaredProperty, Object] other The other
27
+ # attribute definition to compare with.
28
+ #
29
+ # @return [-1, 0, 1, nil]
30
+ def <=>(other)
31
+ return nil unless other.instance_of? self.class
32
+ return nil if name == other.name && options != other.options
33
+ self.to_s <=> other.to_s
34
+ end
35
+
36
+ def inspect
37
+ options_description = options.map { |key, value| "#{key.inspect} => #{value.inspect}" }.sort.join(', ')
38
+ inspected_options = ", #{options_description}" unless options_description.empty?
39
+ "attribute :#{name}#{inspected_options}"
40
+ end
41
+
42
+ def to_s
43
+ name.to_s
44
+ end
45
+
46
+ def to_sym
47
+ name
48
+ end
49
+
50
+ def [](key)
51
+ respond_to?(key) ? public_send(key) : nil
52
+ end
53
+
54
+ def register
55
+ register_magic_properties
56
+ end
57
+
58
+ def type
59
+ options[:type]
60
+ end
61
+
62
+ def typecaster
63
+ options[:typecaster]
64
+ end
65
+
66
+ def default_value
67
+ options[:default]
68
+ end
69
+ alias_method :default, :default_value
70
+
71
+ def fail_invalid_options!
72
+ case
73
+ when index?(:exact) && constraint?(:unique)
74
+ fail Neo4j::InvalidPropertyOptionsError,
75
+ "#Uniqueness constraints also provide exact indexes, cannot set both options on property #{name}"
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ def option_with_value!(key, value)
82
+ options[key] = value
83
+ fail_invalid_options!
84
+ end
85
+
86
+ def option_with_value?(key, value)
87
+ options[key] == value
88
+ end
89
+
90
+ # Tweaks properties
91
+ def register_magic_properties
92
+ options[:type] ||= Neo4j::Config.timestamp_type if timestamp_prop?
93
+
94
+ register_magic_typecaster
95
+ register_type_converter
96
+ end
97
+
98
+ def timestamp_prop?
99
+ name.to_sym == :created_at || name.to_sym == :updated_at
100
+ end
101
+
102
+ def register_magic_typecaster
103
+ found_typecaster = Neo4j::Shared::TypeConverters.typecaster_for(options[:type])
104
+ return unless found_typecaster && found_typecaster.respond_to?(:primitive_type)
105
+ options[:typecaster] = found_typecaster
106
+ @magic_typecaster = options[:type]
107
+ options[:type] = found_typecaster.primitive_type
108
+ end
109
+
110
+ def register_type_converter
111
+ converter = options[:serializer]
112
+ return unless converter
113
+ options[:type] = converter.convert_type
114
+ options[:typecaster] = Neo4j::Shared::TypeConverters::ObjectConverter
115
+ Neo4j::Shared::TypeConverters.register_converter(converter)
116
+ end
117
+ end
118
+ end