neo4j 5.1.5 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ee39f89dbeb6c4cbb84e138646bd14387a58a3e1
4
- data.tar.gz: d3db82c3713b3be64ed389f9e249c95064d68491
3
+ metadata.gz: cdc33103cf05e1ec6a124c615303f0fcb7ff0760
4
+ data.tar.gz: 679ae2889ebbe49d6083fe9d471508996a2e6750
5
5
  SHA512:
6
- metadata.gz: 7761cd3c739a85440143ebf2c0063b797e874a83002639a6fecebc35b24f04013cfda55a798b344fbb5bbccfc85a287340773ca238f3373400cf2488e601ade4
7
- data.tar.gz: 779db34b9d23809daec8ba37bee90ba6207869016f29928b52b7e1f1a5a6b687ff644d70c15ed14a86fcc747047d9b7062a87e57ea33fb0ef0589b7fd2bb0fac
6
+ metadata.gz: 1b770454c98281fded4132b80fd9c3b7e6fc42b9450fa6dbc97d5b6eefd77b98a9c909183cfe05637ce2e7cef3d368d72d5abd06d2dbe690d8b203f527ee0885
7
+ data.tar.gz: 4b46fbd5003efb1421a0fdc5f99fe0511c0c89ed6ff7a9c8d08822d7cb03bd156cb82e526ea44c1608476f4ad4c5c9fc5ed1b1d918a2f8a4988e8b741c874b19
data/CHANGELOG.md CHANGED
@@ -5,17 +5,16 @@ This project adheres to [Semantic Versioning](http://semver.org/).
5
5
 
6
6
  ## [Unreleased][unreleased]
7
7
 
8
- ## [5.1.5] = 08-26-2015
8
+ ## [5.2.0] - 08-30-2015
9
9
 
10
- ### Fixed
11
- - Fixed errors when trying to call `#{association}_ids=` on an unpersisted node with UUIDs or an array thereof.
12
- - Removed extra Cypher query to replace relationships when working with unpersisted nodes and `association=`.
13
- - Bug related to Rails reloading an app and returning nodes without first reinitializing models, resulting in CypherNodes.
14
-
15
- ## [5.1.4] - 08-24-2015
10
+ ### Added
11
+ - `props_for_persistence`, `props_for_create`, `props_for_update` instance methods for all nodes and rels. Each returns a hash with properties appropriate for sending to the database in a Cypher query to create or update an object.
12
+ - Added `record_timestamps` configuration do default all `ActiveNode` and `ActiveRel` models to have `created_at` and `updated_at` timestamps (from #939, thanks @rebecca-eakins)
13
+ - Added `timestamp_type` configuration to specify how timestamps should be stored (from #939, thanks @rebecca-eakins)
16
14
 
17
- ### Fixed
18
- - `AssociationProxy` now responds to `serializable_hash` so that `include` can be used in `render json` in Rails controllers
15
+ ### Changed
16
+ - Methods related to basic node and rel persistence (`save`, `create_model`, `_create_node`, others) were refactored to make the processes simpler, clearer, and slightly faster.
17
+ - Unit test directory structure was rearranged to mirror the `lib` directory.
19
18
 
20
19
  ## [5.1.3] - 08-23-2015
21
20
 
@@ -23,6 +22,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
23
22
  - `has_one` associations are now properly cached (like `has_many` associations)
24
23
  - `QueryProxy` now responds to `#to_ary`. Fixes integration with ActiveModelSerializer gem
25
24
 
25
+
26
26
  ## [5.1.2] - 08-20-2015
27
27
 
28
28
  ### Fixed
@@ -79,10 +79,6 @@ module Neo4j::ActiveNode
79
79
  target.public_send(method_name, *args, &block)
80
80
  end
81
81
 
82
- def serializable_hash(options = {})
83
- to_a.map { |record| record.serializable_hash(options) }
84
- end
85
-
86
82
  private
87
83
 
88
84
  def target_for_missing_method(method_name)
@@ -3,7 +3,6 @@ module Neo4j
3
3
  # Provides a mapping between neo4j labels and Ruby classes
4
4
  module Labels
5
5
  extend ActiveSupport::Concern
6
- include Neo4j::ActiveNode::Labels::Reloading
7
6
 
8
7
  WRAPPED_CLASSES = []
9
8
  MODELS_FOR_LABELS_CACHE = {}
@@ -76,9 +75,17 @@ module Neo4j
76
75
  WRAPPED_CLASSES.clear
77
76
  end
78
77
 
78
+ protected
79
+
79
80
  module ClassMethods
80
81
  include Neo4j::ActiveNode::QueryMethods
81
82
 
83
+ def before_remove_const
84
+ associations.each_value(&:queue_model_refresh!)
85
+ MODELS_FOR_LABELS_CACHE.clear
86
+ WRAPPED_CLASSES.clear
87
+ end
88
+
82
89
  # Returns the object with the specified neo4j id.
83
90
  # @param [String,Integer] id of node to find
84
91
  def find(id)
@@ -23,7 +23,6 @@ module Neo4j::ActiveNode
23
23
  # There's a series of callbacks associated with save.
24
24
  # If any of the before_* callbacks return false the action is cancelled and save returns false.
25
25
  def save(*)
26
- update_magic_properties
27
26
  cascade_save do
28
27
  association_proxy_cache.clear
29
28
  create_or_update
@@ -47,25 +46,32 @@ module Neo4j::ActiveNode
47
46
  # Creates a model with values matching those of the instance attributes and returns its id.
48
47
  # @private
49
48
  # @return true
50
- def create_model(*)
51
- create_magic_properties
52
- set_timestamps
53
- create_magic_properties
54
- properties = self.class.declared_property_manager.convert_properties_to(self, :db, props)
55
- node = _create_node(properties)
49
+ def create_model
50
+ node = _create_node(props_for_create)
56
51
  init_on_load(node, node.props)
57
52
  send_props(@relationship_props) if @relationship_props
58
53
  @relationship_props = @deferred_nodes = nil
59
54
  true
60
55
  end
61
56
 
62
- def _create_node(*args)
63
- session = self.class.neo4j_session
64
- props = self.class.default_property_values(self)
65
- props.merge!(args[0]) if args[0].is_a?(Hash)
66
- set_classname(props)
67
- labels = self.class.mapped_label_names
68
- session.create_node(props, labels)
57
+ # TODO: This does not seem like it should be the responsibility of the node.
58
+ # Creates an unwrapped node in the database.
59
+ # @param [Hash] node_props The type-converted properties to be added to the new node.
60
+ # @param [Array] labels The labels to use for creating the new node.
61
+ # @return [Neo4j::Node] A CypherNode or EmbeddedNode
62
+ def _create_node(node_props, labels = labels_for_create)
63
+ self.class.neo4j_session.create_node(node_props, labels)
64
+ end
65
+
66
+ def inject_primary_key!(converted_props)
67
+ self.class.default_property_values(self).tap do |destination_props|
68
+ destination_props.merge!(converted_props) if converted_props.is_a?(Hash)
69
+ end
70
+ end
71
+
72
+ # @return [Array] Labels to be set on the node during a create event
73
+ def labels_for_create
74
+ self.class.mapped_label_names
69
75
  end
70
76
 
71
77
  private
@@ -164,7 +164,6 @@ module Neo4j
164
164
 
165
165
  # Deletes the relationships between all nodes for the last step in the QueryProxy chain. Executed in the database, callbacks will not be run.
166
166
  def delete_all_rels
167
- return unless start_object && start_object._persisted_obj
168
167
  self.query.delete(rel_var).exec
169
168
  end
170
169
 
@@ -40,10 +40,8 @@ module Neo4j
40
40
  end
41
41
 
42
42
  def save_and_associate_node(association_name, node, operator)
43
- if node.respond_to?(:changed?)
44
- node.save if node.changed? || !node.persisted?
45
- fail "Unable to defer node persistence, could not save #{node.inspect}" unless node.persisted?
46
- end
43
+ node.save if node.changed? || !node.persisted?
44
+ fail "Unable to defer node persistence, could not save #{node.inspect}" unless node.persisted?
47
45
  operator == :<< ? send(association_name).send(operator, node) : send(:"#{association_name}=", node)
48
46
  end
49
47
  end
@@ -55,6 +55,8 @@ module Neo4j
55
55
  end
56
56
 
57
57
  included do
58
+ include Neo4j::Timestamps if Neo4j::Config.record_timestamps
59
+
58
60
  def self.inherited(other)
59
61
  inherit_id_property(other)
60
62
  inherited_indexes(other) if self.respond_to?(:indexed_properties)
@@ -7,13 +7,7 @@ module Neo4j::ActiveRel
7
7
  class ModelClassInvalidError < RuntimeError; end
8
8
  class RelCreateFailedError < RuntimeError; end
9
9
 
10
- # Should probably find a way to not need this
11
- def association_proxy_cache
12
- {}
13
- end
14
-
15
10
  def save(*)
16
- update_magic_properties
17
11
  create_or_update
18
12
  end
19
13
 
@@ -21,12 +15,9 @@ module Neo4j::ActiveRel
21
15
  fail RelInvalidError, self unless save(*args)
22
16
  end
23
17
 
24
- def create_model(*)
18
+ def create_model
25
19
  validate_node_classes!
26
- create_magic_properties
27
- set_timestamps
28
- properties = self.class.declared_property_manager.convert_properties_to(self, :db, props)
29
- rel = _create_rel(from_node, to_node, properties)
20
+ rel = _create_rel(from_node, to_node, props_for_create)
30
21
  return self unless rel.respond_to?(:_persisted_obj)
31
22
  init_on_load(rel._persisted_obj, from_node, to_node, @rel_type)
32
23
  true
@@ -82,8 +73,6 @@ module Neo4j::ActiveRel
82
73
  end
83
74
 
84
75
  def _create_rel(from_node, to_node, props = {})
85
- set_classname(props, true)
86
-
87
76
  if from_node.id.nil? || to_node.id.nil?
88
77
  fail RelCreateFailedError, "Unable to create relationship (id is nil). from_node: #{from_node}, to_node: #{to_node}"
89
78
  end
@@ -44,6 +44,8 @@ module Neo4j
44
44
  end
45
45
 
46
46
  included do
47
+ include Neo4j::Timestamps if Neo4j::Config.record_timestamps
48
+
47
49
  def self.inherited(other)
48
50
  super
49
51
  end
data/lib/neo4j/config.rb CHANGED
@@ -2,19 +2,22 @@ module Neo4j
2
2
  # == Keeps configuration for neo4j
3
3
  #
4
4
  # == Configurations keys
5
- #
6
5
  class Config
7
6
  DEFAULT_FILE = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'config', 'neo4j', 'config.yml'))
8
7
  CLASS_NAME_PROPERTY_KEY = 'class_name_property'
9
8
 
10
9
  class << self
10
+ # In keeping with the Rails convention, this class writer lets you globally configure
11
+ # the incluse of timestamps on your nodes and rels. It defaults to false, requiring manual
12
+ # timestamp inclusion.
13
+ # @return [Boolean] the true/false value specified.
14
+ attr_writer :record_timestamps
11
15
  # @return [Fixnum] The location of the default configuration file.
12
16
  def default_file
13
17
  @default_file ||= DEFAULT_FILE
14
18
  end
15
19
 
16
20
  # Sets the location of the configuration YAML file and old deletes configurations.
17
- #
18
21
  # @param [String] file_path represent the path to the file.
19
22
  def default_file=(file_path)
20
23
  delete_all
@@ -93,6 +96,12 @@ module Neo4j
93
96
  configuration.to_yaml
94
97
  end
95
98
 
99
+ # @return [Boolean] The value of the config variable for including
100
+ # timestamps on all models.
101
+ def record_timestamps
102
+ @record_timestamps ||= false
103
+ end
104
+
96
105
  def class_name_property
97
106
  @_class_name_property = Neo4j::Config[CLASS_NAME_PROPERTY_KEY] || :_classname
98
107
  end
@@ -106,6 +115,11 @@ module Neo4j
106
115
  Neo4j::Config[:module_handling] || :none
107
116
  end
108
117
 
118
+ # @return [Class] The configured timestamps type (e.g. Integer) or the default DateTime.
119
+ def timestamp_type
120
+ Neo4j::Config[:timestamp_type] || DateTime
121
+ end
122
+
109
123
  def association_model_namespace
110
124
  Neo4j::Config[:association_model_namespace] || nil
111
125
  end
data/lib/neo4j/railtie.rb CHANGED
@@ -5,12 +5,6 @@ module Neo4j
5
5
  class Railtie < ::Rails::Railtie
6
6
  config.neo4j = ActiveSupport::OrderedOptions.new
7
7
 
8
- if const_defined?(:ActionDispatch)
9
- ActionDispatch::Reloader.to_prepare do
10
- Neo4j::ActiveNode::Labels::Reloading.reload_models!
11
- end
12
- end
13
-
14
8
  # Add ActiveModel translations to the I18n load_path
15
9
  initializer 'i18n' do
16
10
  config.i18n.load_path += Dir[File.join(File.dirname(__FILE__), '..', '..', '..', 'config', 'locales', '*.{rb,yml}')]
@@ -33,12 +33,16 @@ module Neo4j::Shared
33
33
 
34
34
  # Tweaks properties
35
35
  def register_magic_properties
36
- options[:type] ||= DateTime if name.to_sym == :created_at || name.to_sym == :updated_at
36
+ options[:type] ||= Neo4j::Config.timestamp_type if timestamp_prop?
37
37
 
38
38
  register_magic_typecaster
39
39
  register_type_converter
40
40
  end
41
41
 
42
+ def timestamp_prop?
43
+ name.to_sym == :created_at || name.to_sym == :updated_at
44
+ end
45
+
42
46
  def register_magic_typecaster
43
47
  found_typecaster = Neo4j::Shared::TypeConverters.typecaster_for(options[:type])
44
48
  return unless found_typecaster && found_typecaster.respond_to?(:primitive_type)
@@ -4,13 +4,38 @@ module Neo4j::Shared
4
4
 
5
5
  USES_CLASSNAME = []
6
6
 
7
+ # @return [Hash] Given a node's state, will call the appropriate `props_for_{action}` method.
8
+ def props_for_persistence
9
+ _persisted_obj ? props_for_update : props_for_create
10
+ end
11
+
7
12
  def update_model
8
13
  return if !changed_attributes || changed_attributes.empty?
14
+ _persisted_obj.update_props(props_for_update)
15
+ changed_attributes.clear
16
+ end
9
17
 
18
+ # Returns a hash containing:
19
+ # * All properties and values for insertion in the database
20
+ # * A `uuid` (or equivalent) key and value
21
+ # * A `_classname` property, if one is to be set
22
+ # * Timestamps, if the class is set to include them.
23
+ # Note that the UUID is added to the hash but is not set on the node.
24
+ # The timestamps, by comparison, are set on the node prior to addition in this hash.
25
+ # @return [Hash]
26
+ def props_for_create
27
+ inject_timestamps!
28
+ converted_props = props_for_db(props)
29
+ inject_classname!(converted_props)
30
+ return converted_props unless self.class.respond_to?(:default_property_values)
31
+ inject_primary_key!(converted_props)
32
+ end
33
+
34
+ # @return [Hash] Properties and values, type-converted and timestamped for the database.
35
+ def props_for_update
36
+ update_magic_properties
10
37
  changed_props = attributes.select { |k, _| changed_attributes.include?(k) }
11
- changed_props = self.class.declared_property_manager.convert_properties_to(self, :db, changed_props)
12
- _persisted_obj.update_props(changed_props)
13
- changed_attributes.clear
38
+ props_for_db(changed_props)
14
39
  end
15
40
 
16
41
  # Convenience method to set attribute and #save at the same time
@@ -115,7 +140,7 @@ module Neo4j::Shared
115
140
 
116
141
  def reload
117
142
  return self if new_record?
118
- association_proxy_cache.clear
143
+ association_proxy_cache.clear if respond_to?(:association_proxy_cache)
119
144
  changed_attributes && changed_attributes.clear
120
145
  unless reload_from_database
121
146
  @_deleted = true
@@ -159,11 +184,12 @@ module Neo4j::Shared
159
184
 
160
185
  private
161
186
 
162
- def model_cache_key
163
- self.class.model_name.cache_key
187
+ def props_for_db(props_hash)
188
+ self.class.declared_property_manager.convert_properties_to(self, :db, props_hash)
164
189
  end
165
190
 
166
- def create_magic_properties
191
+ def model_cache_key
192
+ self.class.model_name.cache_key
167
193
  end
168
194
 
169
195
  def update_magic_properties
@@ -171,16 +197,28 @@ module Neo4j::Shared
171
197
  end
172
198
 
173
199
  # Inserts the _classname property into an object's properties during object creation.
174
- def set_classname(props, check_version = true)
200
+ def inject_classname!(props, check_version = true)
175
201
  props[:_classname] = self.class.name if self.class.cached_class?(check_version)
176
202
  end
177
203
 
178
- def set_timestamps
204
+ def set_classname(props, check_version = true)
205
+ warning = 'This method has been replaced with `inject_classname!` and will be removed in a future version'.freeze
206
+ ActiveSupport::Deprecation.warn warning, caller
207
+ inject_classname!(props, check_version)
208
+ end
209
+
210
+ def inject_timestamps!
179
211
  now = DateTime.now
180
212
  self.created_at ||= now if respond_to?(:created_at=)
181
213
  self.updated_at ||= now if respond_to?(:updated_at=)
182
214
  end
183
215
 
216
+ def set_timestamps
217
+ warning = 'This method has been replaced with `inject_timestamps!` and will be removed in a future version'.freeze
218
+ ActiveSupport::Deprecation.warn warning, caller
219
+ inject_timestamps!
220
+ end
221
+
184
222
  module ClassMethods
185
223
  # Determines whether a model should insert a _classname property. This can be used to override the automatic matching of returned
186
224
  # objects to models.
@@ -3,9 +3,7 @@ module Neo4j
3
3
  # This mixin includes a created_at timestamp property
4
4
  module Created
5
5
  extend ActiveSupport::Concern
6
- included do
7
- property :created_at, type: DateTime
8
- end
6
+ included { property :created_at }
9
7
  end
10
8
  end
11
9
  end
@@ -3,9 +3,7 @@ module Neo4j
3
3
  # This mixin includes a updated_at timestamp property
4
4
  module Updated
5
5
  extend ActiveSupport::Concern
6
- included do
7
- property :updated_at, type: DateTime
8
- end
6
+ included { property :updated_at }
9
7
  end
10
8
  end
11
9
  end
data/lib/neo4j/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Neo4j
2
- VERSION = '5.1.5'
2
+ VERSION = '5.2.0'
3
3
  end
data/lib/neo4j.rb CHANGED
@@ -60,7 +60,6 @@ require 'neo4j/active_node/query/query_proxy_enumerable'
60
60
  require 'neo4j/active_node/query/query_proxy_find_in_batches'
61
61
  require 'neo4j/active_node/query/query_proxy_eager_loading'
62
62
  require 'neo4j/active_node/query/query_proxy_link'
63
- require 'neo4j/active_node/labels/reloading'
64
63
  require 'neo4j/active_node/labels'
65
64
  require 'neo4j/active_node/id_property/accessor'
66
65
  require 'neo4j/active_node/id_property'
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: 5.1.5
4
+ version: 5.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Ronge, Brian Underwood, Chris Grigg
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-27 00:00:00.000000000 Z
11
+ date: 2015-08-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: orm_adapter
@@ -236,7 +236,6 @@ files:
236
236
  - lib/neo4j/active_node/id_property/accessor.rb
237
237
  - lib/neo4j/active_node/initialize.rb
238
238
  - lib/neo4j/active_node/labels.rb
239
- - lib/neo4j/active_node/labels/reloading.rb
240
239
  - lib/neo4j/active_node/node_wrapper.rb
241
240
  - lib/neo4j/active_node/orm_adapter.rb
242
241
  - lib/neo4j/active_node/persistence.rb
@@ -323,7 +322,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
323
322
  version: '0'
324
323
  requirements: []
325
324
  rubyforge_project: neo4j
326
- rubygems_version: 2.4.6
325
+ rubygems_version: 2.4.5
327
326
  signing_key:
328
327
  specification_version: 4
329
328
  summary: A graph database for Ruby
@@ -1,21 +0,0 @@
1
- module Neo4j::ActiveNode::Labels
2
- module Reloading
3
- extend ActiveSupport::Concern
4
-
5
- MODELS_TO_RELOAD = []
6
-
7
- def self.reload_models!
8
- MODELS_TO_RELOAD.each(&:constantize)
9
- MODELS_TO_RELOAD.clear
10
- end
11
-
12
- module ClassMethods
13
- def before_remove_const
14
- associations.each_value(&:queue_model_refresh!)
15
- MODELS_FOR_LABELS_CACHE.clear
16
- WRAPPED_CLASSES.each { |c| MODELS_TO_RELOAD << c.name }
17
- WRAPPED_CLASSES.clear
18
- end
19
- end
20
- end
21
- end