neo4j-ruby-driver 6.2.1.beta.1 → 6.2.1.beta.2

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/lib/neo4j/driver/bolt/connection.rb +72 -35
  3. data/lib/neo4j/driver/bolt/handshake.rb +14 -10
  4. data/lib/neo4j/driver/bolt/message/failure.rb +8 -8
  5. data/lib/neo4j/driver/bolt/message/record.rb +1 -1
  6. data/lib/neo4j/driver/bolt/message.rb +1 -1
  7. data/lib/neo4j/driver/bolt/pool.rb +2 -2
  8. data/lib/neo4j/driver/bolt/protocol/v44.rb +1 -1
  9. data/lib/neo4j/driver/bolt/record_buffer.rb +15 -8
  10. data/lib/neo4j/driver/bolt/wire.rb +4 -4
  11. data/lib/neo4j/driver/direct/connection_provider.rb +5 -1
  12. data/lib/neo4j/driver/driver.rb +2 -2
  13. data/lib/neo4j/driver/exceptions/no_such_record_exception.rb +1 -1
  14. data/lib/neo4j/driver/internal/duration_normalizer.rb +4 -2
  15. data/lib/neo4j/driver/internal/validator.rb +7 -3
  16. data/lib/neo4j/driver/packstream/markers.rb +11 -11
  17. data/lib/neo4j/driver/packstream/packer.rb +5 -3
  18. data/lib/neo4j/driver/packstream/unpacker.rb +6 -4
  19. data/lib/neo4j/driver/record.rb +3 -9
  20. data/lib/neo4j/driver/result.rb +12 -12
  21. data/lib/neo4j/driver/routing/load_balancer.rb +29 -13
  22. data/lib/neo4j/driver/routing/routing_table.rb +1 -1
  23. data/lib/neo4j/driver/session.rb +72 -70
  24. data/lib/neo4j/driver/summary/database_info.rb +1 -1
  25. data/lib/neo4j/driver/summary/plan.rb +1 -1
  26. data/lib/neo4j/driver/summary/result_summary.rb +2 -0
  27. data/lib/neo4j/driver/transaction.rb +13 -10
  28. data/lib/neo4j/driver/types/duration.rb +6 -6
  29. data/lib/neo4j/driver/types/entity.rb +40 -0
  30. data/lib/neo4j/driver/types/local_date_time.rb +46 -21
  31. data/lib/neo4j/driver/types/local_time.rb +13 -5
  32. data/lib/neo4j/driver/types/node.rb +5 -14
  33. data/lib/neo4j/driver/types/offset_time.rb +16 -9
  34. data/lib/neo4j/driver/types/path.rb +2 -2
  35. data/lib/neo4j/driver/types/point.rb +4 -4
  36. data/lib/neo4j/driver/types/relationship.rb +6 -15
  37. data/lib/neo4j/driver/types/temporal_value.rb +3 -2
  38. data/lib/neo4j/driver/types/unbound_relationship.rb +5 -10
  39. data/lib/neo4j/driver/version.rb +1 -1
  40. data/lib/neo4j/driver.rb +0 -1
  41. metadata +4 -72
@@ -3,11 +3,17 @@
3
3
  module Neo4j
4
4
  module Driver
5
5
  module Types
6
- # Bolt LocalDateTime — wall-clock datetime without timezone.
7
- # Stored as the wire-format pair (epoch_seconds, nanoseconds), where
8
- # epoch_seconds is the wall-clock components encoded as if they were
9
- # UTC. Two LocalDateTimes representing the same wall-clock value
10
- # always have the same field values regardless of the host TZ.
6
+ # Bolt LocalDateTime — a wall-clock datetime with no timezone
7
+ # (java.time.LocalDateTime). Stored as the wire-format pair
8
+ # (epoch_seconds, nanoseconds), where epoch_seconds encodes the
9
+ # wall-clock components as if they were UTC so the value reads the
10
+ # same regardless of the host TZ.
11
+ #
12
+ # There is deliberately no #to_time: a zone-less value cannot become a
13
+ # zoned Ruby Time without inventing a zone, and every serious datetime
14
+ # library refuses that implicit conversion (Java's LocalDateTime forces
15
+ # .atZone/.atOffset; Python yields a naive datetime). Read the fields via
16
+ # the component getters instead.
11
17
  class LocalDateTime < TemporalValue
12
18
  PARSE_FORMATS = [
13
19
  '%Y-%m-%d %H:%M:%S.%N',
@@ -28,35 +34,54 @@ module Neo4j
28
34
 
29
35
  def self.from_epoch(epoch_seconds, nanoseconds) = new(epoch_seconds, nanoseconds)
30
36
 
37
+ # Store the Time's wall clock (its date and time-of-day *in its own
38
+ # zone*), dropping the zone. We read that wall clock off the instant
39
+ # using the offset the Time already carries — we never invent a zone,
40
+ # so from_time(t) and from_time(t.utc) differ (they show different
41
+ # clocks). epoch_seconds encodes it as-if-UTC per the class contract.
31
42
  def self.from_time(time)
32
- new(time.to_i, ((time.to_f - time.to_i) * NANOS_PER_SECOND).round)
43
+ new(time.to_i + time.utc_offset, time.nsec)
33
44
  end
34
45
 
35
46
  def self.parse(string)
36
47
  # Strip trailing timezone if present — naive datetime ignores it.
37
- naive = string.sub(/[Z+\-]\d{2}:?\d{2}?$/, '')
38
- time = PARSE_FORMATS.lazy.filter_map { |fmt| ::Time.strptime(naive, fmt) rescue nil }.first
48
+ naive = string.sub(/[Z+-]\d{2}:?\d{2}?$/, '')
49
+ time = PARSE_FORMATS.lazy.filter_map do |fmt|
50
+ ::Time.strptime(naive, fmt)
51
+ rescue StandardError
52
+ nil
53
+ end.first
39
54
  raise ArgumentError, "Invalid LocalDateTime format: #{string}" unless time
40
- from_time(::Time.utc(*time_components(time)))
41
- end
42
55
 
43
- def self.time_components(time)
44
- [time.year, time.month, time.day, time.hour, time.min, time.sec, time.subsec * 1_000_000]
56
+ from_time(time)
45
57
  end
46
- private_class_method :time_components
47
58
 
48
- def to_time
49
- ::Time.at(@epoch_seconds, @nanoseconds.to_i, :nanosecond)
50
- end
59
+ def year = wall_clock.year
60
+ def month = wall_clock.month
61
+ def day = wall_clock.day
62
+ def hour = wall_clock.hour
63
+ def minute = wall_clock.min
64
+ def second = wall_clock.sec
65
+ def nanosecond = @nanoseconds
51
66
 
52
- # Add a numeric or ActiveSupport::Duration. Sub-second precision
53
- # preserved by going through to_f.
54
- def +(seconds)
55
- self.class.from_time(to_time + seconds.to_f)
67
+ # Add a numeric or ActiveSupport::Duration. A wall clock has no DST,
68
+ # so this is plain second arithmetic; sub-second precision preserved.
69
+ def +(other)
70
+ total = (@epoch_seconds * NANOS_PER_SECOND) + @nanoseconds +
71
+ (other.to_f * NANOS_PER_SECOND).round
72
+ self.class.new(total / NANOS_PER_SECOND, total % NANOS_PER_SECOND)
56
73
  end
57
74
 
58
75
  def to_s
59
- to_time.strftime('%Y-%m-%d %H:%M:%S.%N')
76
+ wall_clock.strftime('%Y-%m-%d %H:%M:%S.%N')
77
+ end
78
+
79
+ private
80
+
81
+ # epoch_seconds read back as its wall clock. UTC by construction, so
82
+ # the components never depend on the host TZ.
83
+ def wall_clock
84
+ ::Time.at(@epoch_seconds, @nanoseconds.to_i, :nanosecond, in: 'UTC')
60
85
  end
61
86
  end
62
87
  end
@@ -16,6 +16,14 @@ module Neo4j
16
16
 
17
17
  def self.from_nanos(nanoseconds) = new(nanoseconds)
18
18
 
19
+ # Build from a Ruby Time, taking its displayed wall clock (so the
20
+ # host zone / .utc of the Time decides which time-of-day is captured).
21
+ # Mirrors LocalDateTime.from_time.
22
+ def self.from_time(time)
23
+ new(time.hour * NANOS_PER_HOUR + time.min * NANOS_PER_MINUTE +
24
+ time.sec * NANOS_PER_SECOND + time.nsec)
25
+ end
26
+
19
27
  # Accepts "12:34:56.123456789" or any string containing such a
20
28
  # time component (so a full datetime works too — we just take the
21
29
  # time fields).
@@ -34,15 +42,15 @@ module Neo4j
34
42
  end
35
43
  private_class_method :parse_nanos
36
44
 
37
- def hour = (@nanoseconds / NANOS_PER_HOUR) % 24
38
- def minute = (@nanoseconds / NANOS_PER_MINUTE) % 60
39
- def second = (@nanoseconds / NANOS_PER_SECOND) % 60
45
+ def hour = (@nanoseconds / NANOS_PER_HOUR) % 24
46
+ def minute = (@nanoseconds / NANOS_PER_MINUTE) % 60
47
+ def second = (@nanoseconds / NANOS_PER_SECOND) % 60
40
48
  def nanosecond = @nanoseconds % NANOS_PER_SECOND
41
49
 
42
50
  # Add a numeric or ActiveSupport::Duration. Sub-second precision
43
51
  # preserved by going through to_f (NOT to_i, which dropped 0.5s).
44
- def +(seconds)
45
- self.class.new(@nanoseconds + (seconds.to_f * NANOS_PER_SECOND).round)
52
+ def +(other)
53
+ self.class.new(@nanoseconds + (other.to_f * NANOS_PER_SECOND).round)
46
54
  end
47
55
 
48
56
  def to_s
@@ -3,23 +3,14 @@
3
3
  module Neo4j
4
4
  module Driver
5
5
  module Types
6
- # Represents a Node in the Neo4j graph
7
- class Node
8
- attr_reader :id, :labels, :properties, :element_id
6
+ # Represents a Node in the Neo4j graph. Mirrors
7
+ # org.neo4j.driver.types.Node — an Entity with a set of labels.
8
+ class Node < Entity
9
+ attr_reader :labels
9
10
 
10
11
  def initialize(id, labels, properties, element_id = nil)
11
- @id = id
12
+ super(id, properties, element_id)
12
13
  @labels = labels
13
- @properties = properties
14
- @element_id = element_id || id.to_s
15
- end
16
-
17
- def [](key)
18
- @properties[key.to_s] || @properties[key.to_sym]
19
- end
20
-
21
- def ==(other)
22
- other.is_a?(Node) && other.id == @id
23
14
  end
24
15
  end
25
16
  end
@@ -19,10 +19,16 @@ module Neo4j
19
19
 
20
20
  def self.from_nanos(nanoseconds, tz_offset_seconds) = new(nanoseconds, tz_offset_seconds)
21
21
 
22
+ # Build from a Ruby Time — its wall clock plus its UTC offset.
23
+ # Reuses LocalTime's field extraction. Mirrors LocalDateTime.from_time.
24
+ def self.from_time(time)
25
+ new(LocalTime.from_time(time).nanoseconds, time.utc_offset)
26
+ end
27
+
22
28
  # Accepts "12:34:56.123456789+01:00", "12:34:56Z", or any string
23
29
  # containing such a time-with-offset component.
24
30
  def self.parse(string)
25
- match = string.match(/(\d{1,2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?([Z+\-][\d:]*)?/)
31
+ match = string.match(/(\d{1,2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?([Z+-][\d:]*)?/)
26
32
  raise ArgumentError, "Invalid OffsetTime format: #{string}" unless match
27
33
 
28
34
  nanos = LocalTime.send(:parse_nanos,
@@ -32,22 +38,22 @@ module Neo4j
32
38
 
33
39
  def self.parse_offset(str)
34
40
  return 0 if str == 'Z'
35
- raise ArgumentError, "Invalid timezone offset: #{str}" unless str =~ /^([+\-])(\d{2}):?(\d{2})?$/
41
+ raise ArgumentError, "Invalid timezone offset: #{str}" unless str =~ /^([+-])(\d{2}):?(\d{2})?$/
36
42
 
37
- sign = $1 == '+' ? 1 : -1
38
- sign * ($2.to_i * 3600 + ($3 || 0).to_i * 60)
43
+ sign = ::Regexp.last_match(1) == '+' ? 1 : -1
44
+ sign * (::Regexp.last_match(2).to_i * 3600 + (::Regexp.last_match(3) || 0).to_i * 60)
39
45
  end
40
46
  private_class_method :parse_offset
41
47
 
42
- def hour = (@nanoseconds / NANOS_PER_HOUR) % 24
43
- def minute = (@nanoseconds / NANOS_PER_MINUTE) % 60
44
- def second = (@nanoseconds / NANOS_PER_SECOND) % 60
48
+ def hour = (@nanoseconds / NANOS_PER_HOUR) % 24
49
+ def minute = (@nanoseconds / NANOS_PER_MINUTE) % 60
50
+ def second = (@nanoseconds / NANOS_PER_SECOND) % 60
45
51
  def nanosecond = @nanoseconds % NANOS_PER_SECOND
46
52
 
47
53
  # Add a numeric or ActiveSupport::Duration. Sub-second precision
48
54
  # preserved (see LocalTime#+).
49
- def +(seconds)
50
- self.class.new(@nanoseconds + (seconds.to_f * NANOS_PER_SECOND).round, @tz_offset_seconds)
55
+ def +(other)
56
+ self.class.new(@nanoseconds + (other.to_f * NANOS_PER_SECOND).round, @tz_offset_seconds)
51
57
  end
52
58
 
53
59
  def to_s
@@ -66,6 +72,7 @@ module Neo4j
66
72
  # <=>; == still requires same fields (they're not the same value).
67
73
  def <=>(other)
68
74
  return nil unless other.is_a?(OffsetTime)
75
+
69
76
  utc_nanos <=> other.utc_nanos
70
77
  end
71
78
 
@@ -45,8 +45,8 @@ module Neo4j
45
45
 
46
46
  # Iterate over segments in the path
47
47
  # Each segment represents a relationship and its start/end nodes
48
- def each(&block)
49
- @segments.each(&block)
48
+ def each(&)
49
+ @segments.each(&)
50
50
  end
51
51
 
52
52
  # Represents a segment of a path (a relationship and its start/end nodes)
@@ -8,10 +8,10 @@ module Neo4j
8
8
  attr_reader :srid, :x, :y, :z
9
9
 
10
10
  # SRID constants for coordinate reference systems
11
- WGS_84_2D = 4326 # Geographic 2D (longitude, latitude)
12
- WGS_84_3D = 4979 # Geographic 3D (longitude, latitude, height)
13
- CARTESIAN_2D = 7203 # Cartesian 2D (x, y)
14
- CARTESIAN_3D = 9157 # Cartesian 3D (x, y, z)
11
+ WGS_84_2D = 4326 # Geographic 2D (longitude, latitude)
12
+ WGS_84_3D = 4979 # Geographic 3D (longitude, latitude, height)
13
+ CARTESIAN_2D = 7203 # Cartesian 2D (x, y)
14
+ CARTESIAN_3D = 9157 # Cartesian 3D (x, y, z)
15
15
 
16
16
  def initialize(srid: nil, x: nil, y: nil, z: nil, longitude: nil, latitude: nil, height: nil)
17
17
  # Handle longitude/latitude aliases for x/y
@@ -4,10 +4,11 @@ module Neo4j
4
4
  module Driver
5
5
  module Types
6
6
  # Represents a Relationship in the Neo4j graph.
7
- # Mirrors Java's org.neo4j.driver.types.Relationship.
8
- class Relationship
9
- attr_reader :id, :start_node_id, :end_node_id, :type, :properties,
10
- :element_id, :start_node_element_id, :end_node_element_id
7
+ # Mirrors Java's org.neo4j.driver.types.Relationship — an Entity with a
8
+ # type and start/end node ids.
9
+ class Relationship < Entity
10
+ attr_reader :start_node_id, :end_node_id, :type,
11
+ :start_node_element_id, :end_node_element_id
11
12
 
12
13
  # Bolt 4.x wire doesn't include start/end_node_element_id —
13
14
  # PackStream hydration passes nil for those slots. Fall back to
@@ -18,23 +19,13 @@ module Neo4j
18
19
  def initialize(id, start_node_id, end_node_id, type, properties,
19
20
  element_id = nil, start_node_element_id = nil,
20
21
  end_node_element_id = nil)
21
- @id = id
22
+ super(id, properties, element_id)
22
23
  @start_node_id = start_node_id
23
24
  @end_node_id = end_node_id
24
25
  @type = type
25
- @properties = properties
26
- @element_id = element_id || id.to_s
27
26
  @start_node_element_id = start_node_element_id || start_node_id.to_s
28
27
  @end_node_element_id = end_node_element_id || end_node_id.to_s
29
28
  end
30
-
31
- def [](key)
32
- @properties[key.to_s] || @properties[key.to_sym]
33
- end
34
-
35
- def ==(other)
36
- other.is_a?(Relationship) && other.id == @id
37
- end
38
29
  end
39
30
  end
40
31
  end
@@ -13,8 +13,8 @@ module Neo4j
13
13
 
14
14
  NANOS_PER_SECOND = 1_000_000_000
15
15
  NANOS_PER_MINUTE = 60 * NANOS_PER_SECOND
16
- NANOS_PER_HOUR = 60 * NANOS_PER_MINUTE
17
- NANOS_PER_DAY = 24 * NANOS_PER_HOUR
16
+ NANOS_PER_HOUR = 60 * NANOS_PER_MINUTE
17
+ NANOS_PER_DAY = 24 * NANOS_PER_HOUR
18
18
 
19
19
  def self.significant_fields
20
20
  raise NotImplementedError, "#{self} must define .significant_fields"
@@ -26,6 +26,7 @@ module Neo4j
26
26
 
27
27
  def <=>(other)
28
28
  return nil unless other.is_a?(self.class)
29
+
29
30
  significant <=> other.significant
30
31
  end
31
32
 
@@ -3,19 +3,14 @@
3
3
  module Neo4j
4
4
  module Driver
5
5
  module Types
6
- # Represents an unbound relationship (used in paths before binding to nodes)
7
- class UnboundRelationship
8
- attr_reader :id, :type, :properties, :element_id
6
+ # Represents an unbound relationship (used in paths before binding to
7
+ # nodes). An Entity with a type but no start/end nodes yet.
8
+ class UnboundRelationship < Entity
9
+ attr_reader :type
9
10
 
10
11
  def initialize(id, type, properties, element_id = nil)
11
- @id = id
12
+ super(id, properties, element_id)
12
13
  @type = type
13
- @properties = properties
14
- @element_id = element_id || id.to_s
15
- end
16
-
17
- def [](key)
18
- @properties[key.to_s] || @properties[key.to_sym]
19
14
  end
20
15
 
21
16
  # Bind this relationship to specific start and end nodes
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Neo4j
4
4
  module Driver
5
- VERSION = '6.2.1.beta.1'
5
+ VERSION = '6.2.1.beta.2'
6
6
  end
7
7
  end
data/lib/neo4j/driver.rb CHANGED
@@ -3,7 +3,6 @@
3
3
  require 'connection_pool'
4
4
  require 'neo4j-ruby-driver_loader'
5
5
  require 'openssl'
6
- require 'set'
7
6
  require 'socket'
8
7
  require 'stringio'
9
8
  require 'tzinfo'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: neo4j-ruby-driver
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.2.1.beta.1
4
+ version: 6.2.1.beta.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Neo4j Driver Team
@@ -37,76 +37,6 @@ dependencies:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
39
  version: '2.6'
40
- - !ruby/object:Gem::Dependency
41
- name: csv
42
- requirement: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - ">="
45
- - !ruby/object:Gem::Version
46
- version: '0'
47
- type: :development
48
- prerelease: false
49
- version_requirements: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - ">="
52
- - !ruby/object:Gem::Version
53
- version: '0'
54
- - !ruby/object:Gem::Dependency
55
- name: ffaker
56
- requirement: !ruby/object:Gem::Requirement
57
- requirements:
58
- - - ">="
59
- - !ruby/object:Gem::Version
60
- version: '0'
61
- type: :development
62
- prerelease: false
63
- version_requirements: !ruby/object:Gem::Requirement
64
- requirements:
65
- - - ">="
66
- - !ruby/object:Gem::Version
67
- version: '0'
68
- - !ruby/object:Gem::Dependency
69
- name: rake
70
- requirement: !ruby/object:Gem::Requirement
71
- requirements:
72
- - - "~>"
73
- - !ruby/object:Gem::Version
74
- version: '13.0'
75
- type: :development
76
- prerelease: false
77
- version_requirements: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - "~>"
80
- - !ruby/object:Gem::Version
81
- version: '13.0'
82
- - !ruby/object:Gem::Dependency
83
- name: rspec
84
- requirement: !ruby/object:Gem::Requirement
85
- requirements:
86
- - - "~>"
87
- - !ruby/object:Gem::Version
88
- version: '3.13'
89
- type: :development
90
- prerelease: false
91
- version_requirements: !ruby/object:Gem::Requirement
92
- requirements:
93
- - - "~>"
94
- - !ruby/object:Gem::Version
95
- version: '3.13'
96
- - !ruby/object:Gem::Dependency
97
- name: rspec-its
98
- requirement: !ruby/object:Gem::Requirement
99
- requirements:
100
- - - "~>"
101
- - !ruby/object:Gem::Version
102
- version: '2.0'
103
- type: :development
104
- prerelease: false
105
- version_requirements: !ruby/object:Gem::Requirement
106
- requirements:
107
- - - "~>"
108
- - !ruby/object:Gem::Version
109
- version: '2.0'
110
40
  - !ruby/object:Gem::Dependency
111
41
  name: connection_pool
112
42
  requirement: !ruby/object:Gem::Requirement
@@ -245,6 +175,7 @@ files:
245
175
  - lib/neo4j/driver/summary/summary_counters.rb
246
176
  - lib/neo4j/driver/transaction.rb
247
177
  - lib/neo4j/driver/types/duration.rb
178
+ - lib/neo4j/driver/types/entity.rb
248
179
  - lib/neo4j/driver/types/local_date_time.rb
249
180
  - lib/neo4j/driver/types/local_time.rb
250
181
  - lib/neo4j/driver/types/node.rb
@@ -262,7 +193,8 @@ files:
262
193
  homepage: https://github.com/neo4jrb/neo4j-ruby-driver
263
194
  licenses:
264
195
  - MIT
265
- metadata: {}
196
+ metadata:
197
+ rubygems_mfa_required: 'true'
266
198
  rdoc_options: []
267
199
  require_paths:
268
200
  - lib