neo4j_bolt 0.4.2 → 0.4.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 66da85ac04ffd0ef43fea4fba9a676ea2168770f85825573652a58dbc0a48f2b
4
- data.tar.gz: 132911b813e88d5a3efb590fe5cd29da0c8adb6663b06b9931bb0a03f4fcae9c
3
+ metadata.gz: e6322e3f763f052647f81029aaa96e45d6cb6f41a8e8d625c198d025b1408a81
4
+ data.tar.gz: ddf55ab34e93f5b65b36273a2c148037f4455e18252aba692296e52598668902
5
5
  SHA512:
6
- metadata.gz: 37e9205800f8661683bc246c6a962a15254c2d1e4a33d5b948f85d5e59d9ac38911e67c4a2e18bb318bc9f3ceeb3eedb7199004e98b0ce1694ae2e0f16f9ce8e
7
- data.tar.gz: 3f7066485f251b5939b9a448b441290f16714311ebd7baa2b2baa27d785ae2463902c63b6c14b532df5adfe574242c8e415305967677110d92943a431e4360d3
6
+ metadata.gz: bff514f49ee709624ac1b178e2216edd001ad168845c397aff2f9d28a3d3752a4c3fcbf0d7f9ea4bc6c0a1f68ffd6feef8fae560127774c0aa0277a4d6cf6ffd
7
+ data.tar.gz: d5ef2dbebc19f6f1538ee461993ec20a9184b734ba4b490ca6a941f4de7b2cb526f22ad59daa9f8578afee00f0be25728c16eed29f6ddfab1a581901b8eabb42
data/README.md CHANGED
@@ -4,14 +4,14 @@ Neo4jBolt 0.4 is a small compatibility and convenience layer for Ruby applicatio
4
4
 
5
5
  Neo4jBolt no longer implements the Bolt wire protocol itself.
6
6
 
7
- `0.4.0` requires Ruby 3.4 or newer and pins `neo4j-ruby-driver` to `6.2.1.beta.4`. Applications on older Rubies can remain on the Neo4jBolt 0.3.x line; this prerelease is intentionally not an automatic upgrade for them.
7
+ `0.4.4` requires Ruby 3.4 or newer and pins `neo4j-ruby-driver` to `6.2.1.beta.4`. Applications on older Rubies can remain on the Neo4jBolt 0.3.x line; this prerelease is intentionally not an automatic upgrade for them.
8
8
 
9
9
  ## Installation
10
10
 
11
11
  For this prerelease, specify the version explicitly:
12
12
 
13
13
  ```ruby
14
- gem "neo4j_bolt", "0.4.0"
14
+ gem "neo4j_bolt", "0.4.4"
15
15
  ```
16
16
 
17
17
  Then run `bundle install`. A running Neo4j database is required.
@@ -26,6 +26,24 @@ Neo4jBolt.bolt_port = 7687
26
26
  Neo4jBolt.bolt_verbosity = 0
27
27
  ```
28
28
 
29
+ Authentication and database selection can use the standard Neo4j client environment variables:
30
+
31
+ ```bash
32
+ export NEO4J_USERNAME=neo4j
33
+ export NEO4J_PASSWORD=secret
34
+ export NEO4J_DATABASE=neo4j
35
+ ```
36
+
37
+ They are read when `neo4j_bolt` is loaded. Explicit Ruby configuration overrides those defaults:
38
+
39
+ ```ruby
40
+ Neo4jBolt.bolt_username = "neo4j"
41
+ Neo4jBolt.bolt_password = "secret"
42
+ Neo4jBolt.bolt_database = "movies"
43
+ ```
44
+
45
+ If both username and password are unset, Neo4jBolt keeps the existing unauthenticated connection behavior. If only one is set, the first query raises `Neo4jBolt::Error`. Leaving the database unset lets Neo4j choose the user's/server's default database.
46
+
29
47
  Applications can include the module as before:
30
48
 
31
49
  ```ruby
@@ -36,7 +54,7 @@ They can also extend another object or call the convenience methods directly on
36
54
 
37
55
  One lazily initialized upstream driver owns a thread-safe connection pool. Each standalone query uses a short-lived session and returns its connection to that pool after its result has been consumed. Queries from several Puma threads are not globally serialized.
38
56
 
39
- `cleanup_neo4j` safely retires the driver and its pool after active operations finish. A later query creates a new driver. Do not call cleanup from inside an active query or transaction on the same thread.
57
+ `cleanup_neo4j` safely retires the driver and its pool after active operations finish. A later query creates a new driver. Changing host, port, username, or password also retires the pool so later connections use the new settings. Database selection is a session setting and therefore applies to newly opened sessions without rebuilding the driver. Do not call cleanup from inside an active query or transaction on the same thread.
40
58
 
41
59
  ## Running queries
42
60
 
@@ -159,6 +177,13 @@ end
159
177
 
160
178
  Passing `progress_io:` is optional for the Ruby API. The CLI always reports dump/load progress on stderr, so dump data written to stdout remains safe to redirect or pipe. In `auto` mode a terminal gets a colored, in-place progress bar while redirected stderr receives periodic plain-text progress lines. Set `NEO4J_BOLT_PROGRESS=pretty` to force the terminal display through a container or other wrapper that hides the TTY, or `NEO4J_BOLT_PROGRESS=plain` to force log-friendly output. `NO_COLOR` disables colors without disabling the in-place display.
161
179
 
180
+ Both `dump` and `load` accept `--color COLOR` to choose the spinner and filled progress-bar accent. The default is `cyan`. The Ruby API exposes the same setting as `progress_color:`. Available colors are `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `white`, `bright-red`, `bright-green`, `bright-yellow`, `bright-blue`, `bright-magenta`, `bright-cyan`, and `bright-white`.
181
+
182
+ ```bash
183
+ neo4j_bolt dump --color magenta -o database.dump
184
+ neo4j_bolt load --color bright-blue database.dump
185
+ ```
186
+
162
187
  The `0, 1, 2, ...` IDs are synthetic dump-local IDs. Database-internal IDs and element IDs are never written to the persistent format. Dumping keeps the count, node, and relationship reads in one transaction, so the driver-provided entity identity used to connect the two streamed result sets stays within Neo4j's transaction-scoped identity guarantee. To keep dump numbering deterministic, ordering uses `elementId()` on modern Neo4j and an internal `id()` fallback only on Neo4j 4.4, where `elementId()` does not exist. Old Neo4jBolt dumps remain loadable.
163
188
 
164
189
  For relational loads, the adapter assigns a random temporary label and dump-ID property to imported nodes. After the nodes are committed it builds a temporary `neo4j_bolt_` index on that property, uses indexed lookups while creating relationships, drops the index, and removes the temporary metadata in batches. No database-internal identity is carried from one load transaction to another. Loading a relational dump therefore requires permission to create and drop an index.
@@ -175,13 +200,13 @@ The `neo4j_bolt` executable retains these commands:
175
200
  | --- | --- |
176
201
  | `neo4j_bolt console` | Open an IRB console with Neo4jBolt loaded |
177
202
  | `neo4j_bolt clear --srsly` | Delete all nodes and relationships |
178
- | `neo4j_bolt dump` | Write the textual database dump |
179
- | `neo4j_bolt load [--force] [--batch-size N] PATH` | Load a textual dump |
203
+ | `neo4j_bolt dump [--color COLOR]` | Write the textual database dump |
204
+ | `neo4j_bolt load [--force] [--batch-size N] [--color COLOR] PATH` | Load a textual dump |
180
205
  | `neo4j_bolt index ls` | List constraints and indexes |
181
206
  | `neo4j_bolt index rm --force` | Remove all constraints and indexes |
182
207
  | `neo4j_bolt visualize` | Generate a GraphViz document |
183
208
 
184
- Use `--host HOST:PORT` to select a server. `gli` remains a runtime dependency for the CLI, and `pry` remains for the separate `bin/console` executable.
209
+ Use `--host HOST:PORT`, `--username USER`, `--password PASSWORD`, and `--database DATABASE` to override connection settings. Username, password, and database otherwise inherit `NEO4J_USERNAME`, `NEO4J_PASSWORD`, and `NEO4J_DATABASE`. Prefer the password environment variable for routine use because command-line arguments may be visible in shell history or process listings. `gli` remains a runtime dependency for the CLI, and `pry` remains for the separate `bin/console` executable.
185
210
 
186
211
  ## Tested Neo4j versions
187
212
 
@@ -191,7 +216,7 @@ The same complete integration suite is run against these exact Community images:
191
216
  - `neo4j:5.26.28-community`
192
217
  - `neo4j:2026.06.0-community`
193
218
 
194
- No compatibility beyond this matrix is claimed for `0.4.0`.
219
+ No compatibility beyond this matrix is claimed for `0.4.4`.
195
220
 
196
221
  Run one modern LTS target:
197
222
 
@@ -205,15 +230,16 @@ Run the complete sequential matrix:
205
230
  bundle exec rake spec:matrix
206
231
  ```
207
232
 
208
- The harness creates uniquely named disposable containers with `NEO4J_AUTH=none`, dynamically publishes Bolt ports, waits by establishing a real driver/query connection, and cleans every container through shell traps. A developer does not need to start Neo4j manually.
233
+ The harness creates uniquely named disposable containers with Basic authentication enabled, dynamically publishes Bolt ports, waits by establishing a real driver/query connection, and cleans every container through shell traps. A developer does not need to start Neo4j manually.
209
234
 
210
- To point RSpec itself at an already disposable database, set `NEO4J_BOLT_TEST_HOST` and `NEO4J_BOLT_TEST_PORT`. The explicit port requirement protects real databases from the destructive integration suite.
235
+ To point RSpec itself at an already disposable database, set `NEO4J_BOLT_TEST_HOST` and `NEO4J_BOLT_TEST_PORT`. If it requires authentication or an explicit database, also set `NEO4J_USERNAME`, `NEO4J_PASSWORD`, and `NEO4J_DATABASE`. The explicit port requirement protects real databases from the destructive integration suite.
211
236
 
212
237
  ## Compatibility inventory for 0.4
213
238
 
214
239
  | API | Status |
215
240
  | --- | --- |
216
241
  | `bolt_host`, `bolt_port`, `bolt_verbosity` | Preserved |
242
+ | `bolt_username`, `bolt_password`, `bolt_database` | Additive authentication/database configuration |
217
243
  | Included/extended/module-style use | Preserved |
218
244
  | `neo4j_query`, including incremental block form | Preserved via adapter |
219
245
  | `neo4j_query_expect_one` | Preserved via adapter |
@@ -228,12 +254,13 @@ To point RSpec itself at an already disposable database, set `NEO4J_BOLT_TEST_HO
228
254
  | CLI commands | Preserved |
229
255
  | `BoltSocket`, `BoltBuffer`, protocol markers/state/parser/packer | Intentionally removed private implementation details |
230
256
 
231
- ## 0.4.0 migration notes
257
+ ## 0.4.4 migration notes
232
258
 
233
259
  - Ruby 3.4 or newer is required; Ruby 2.x/3.0–3.3 applications should stay on 0.3.x until upgraded.
234
260
  - The exact prerelease upstream dependency is pinned while no stable `neo4j-ruby-driver` 6.2.x exists.
235
261
  - Connections are pooled and safe for concurrent use instead of one mutable socket per including object.
236
- - TLS, routing, authentication, database selection, and other upstream-driver configuration are not newly exposed through the legacy three-setting API in this compatibility prerelease.
262
+ - Basic authentication and database selection are now exposed through `bolt_username`, `bolt_password`, and `bolt_database`, with `NEO4J_USERNAME`, `NEO4J_PASSWORD`, and `NEO4J_DATABASE` as environment defaults.
263
+ - TLS, routing, URI schemes, and other upstream-driver configuration are still not exposed through the compatibility API.
237
264
  - `BoltSocket`, `BoltBuffer`, `ServerState`, `BoltMarker`, `UnexpectedServerResponse`, `State`, and `CypherError` were undocumented wire internals and are removed.
238
265
 
239
266
  ## Development
data/bin/neo4j_bolt CHANGED
@@ -22,12 +22,18 @@ class App
22
22
 
23
23
  flag [:v, :verbosity], :default_value => 0
24
24
  flag [:h, :host], :default_value => 'localhost:7687'
25
+ flag [:u, :username], :default_value => nil, :desc => 'Neo4j username (default: NEO4J_USERNAME)'
26
+ flag [:p, :password], :default_value => nil, :desc => 'Neo4j password (default: NEO4J_PASSWORD)'
27
+ flag [:d, :database], :default_value => nil, :desc => 'Neo4j database (default: NEO4J_DATABASE)'
25
28
 
26
29
  pre do |global_options, command, options, args|
27
30
  host = global_options[:host]
28
31
  Neo4jBolt.bolt_host = host.split(':').first
29
32
  Neo4jBolt.bolt_port = host.split(':').last.to_i
30
33
  Neo4jBolt.bolt_verbosity = global_options[:verbosity].to_i
34
+ Neo4jBolt.bolt_username = global_options[:username] unless global_options[:username].nil?
35
+ Neo4jBolt.bolt_password = global_options[:password] unless global_options[:password].nil?
36
+ Neo4jBolt.bolt_database = global_options[:database] unless global_options[:database].nil?
31
37
  true
32
38
  end
33
39
 
@@ -47,9 +53,10 @@ class App
47
53
  long_desc 'Dump all nodes and relationships.'
48
54
  command :dump do |c|
49
55
  c.flag [:o, 'out-file'.to_sym], :default_value => '/dev/stdout'
56
+ c.flag [:color], :default_value => 'cyan', :desc => 'progress accent color'
50
57
  c.action do |global_options, options|
51
58
  File.open(options['out-file'.to_sym], 'w') do |f|
52
- dump_database(f, progress_io: $stderr)
59
+ dump_database(f, progress_io: $stderr, progress_color: options[:color])
53
60
  end
54
61
  end
55
62
  end
@@ -63,6 +70,7 @@ class App
63
70
  c.switch [:f, :force], :default_value => false, :desc => 'force appending nodes even if the database is not empty'
64
71
  c.flag [:b, 'batch-size'.to_sym], :default_value => Neo4jBolt::LOAD_INITIAL_BATCH_SIZE,
65
72
  :desc => 'initial batch size; automatically reduced on transaction-memory errors'
73
+ c.flag [:color], :default_value => 'cyan', :desc => 'progress accent color'
66
74
  c.action do |global_options, options, args|
67
75
  help_now!('input path is required') if args.empty?
68
76
  path = args.shift
@@ -71,6 +79,7 @@ class App
71
79
  f,
72
80
  force_append: options[:force],
73
81
  progress_io: $stderr,
82
+ progress_color: options[:color],
74
83
  initial_batch_size: options['batch-size'.to_sym]
75
84
  )
76
85
  end
@@ -1,3 +1,3 @@
1
1
  module Neo4jBolt
2
- VERSION = "0.4.2"
2
+ VERSION = "0.4.4"
3
3
  end
data/lib/neo4j_bolt.rb CHANGED
@@ -29,13 +29,35 @@ module Neo4jBolt
29
29
  reset: "\e[0m",
30
30
  bold: "\e[1m",
31
31
  dim: "\e[2m",
32
- cyan: "\e[36m",
32
+ red: "\e[31m",
33
33
  green: "\e[32m",
34
- yellow: "\e[33m"
34
+ yellow: "\e[33m",
35
+ blue: "\e[34m",
36
+ magenta: "\e[35m",
37
+ cyan: "\e[36m",
38
+ white: "\e[37m",
39
+ bright_red: "\e[91m",
40
+ bright_green: "\e[92m",
41
+ bright_yellow: "\e[93m",
42
+ bright_blue: "\e[94m",
43
+ bright_magenta: "\e[95m",
44
+ bright_cyan: "\e[96m",
45
+ bright_white: "\e[97m"
46
+ }.freeze
47
+ ACCENT_STYLES = {
48
+ "red" => :red, "green" => :green, "yellow" => :yellow, "blue" => :blue,
49
+ "magenta" => :magenta, "cyan" => :cyan, "white" => :white,
50
+ "bright-red" => :bright_red, "bright-green" => :bright_green,
51
+ "bright-yellow" => :bright_yellow, "bright-blue" => :bright_blue,
52
+ "bright-magenta" => :bright_magenta, "bright-cyan" => :bright_cyan,
53
+ "bright-white" => :bright_white
35
54
  }.freeze
36
55
 
37
- def initialize(io)
56
+ def initialize(io, color: "cyan")
38
57
  @io = io
58
+ @accent_style = ACCENT_STYLES.fetch(color.to_s) do
59
+ raise ArgumentError, "progress color must be one of: #{ACCENT_STYLES.keys.join(', ')}"
60
+ end
39
61
  mode = ENV.fetch("NEO4J_BOLT_PROGRESS", "auto")
40
62
  unless %w[auto pretty plain].include?(mode)
41
63
  raise ArgumentError, "NEO4J_BOLT_PROGRESS must be auto, pretty, or plain"
@@ -110,7 +132,7 @@ module Neo4jBolt
110
132
  spinner = SPINNER[@spinner_index % SPINNER.length]
111
133
  @spinner_index += 1
112
134
 
113
- parts = [color(:cyan, spinner), color(:bold, label)]
135
+ parts = [color(@accent_style, spinner), color(:bold, label)]
114
136
  if percent
115
137
  percent = [[percent.to_i, 0].max, 100].min
116
138
  parts << progress_bar(percent)
@@ -123,7 +145,7 @@ module Neo4jBolt
123
145
  def progress_bar(percent)
124
146
  filled = (percent * BAR_WIDTH / 100.0).round
125
147
  empty = BAR_WIDTH - filled
126
- "#{color(:cyan, '━' * filled)}#{color(:dim, '─' * empty)}"
148
+ "#{color(@accent_style, '━' * filled)}#{color(:dim, '─' * empty)}"
127
149
  end
128
150
 
129
151
  def terminal_parts(message, strip_percent: false)
@@ -313,10 +335,22 @@ module Neo4jBolt
313
335
  host = "[#{host}]" if host.include?(":") && !host.start_with?("[")
314
336
  Neo4j::Driver::GraphDatabase.driver(
315
337
  "bolt://#{host}:#{Neo4jBolt.bolt_port}",
316
- Neo4j::Driver::AuthTokens.none
338
+ authentication_token
317
339
  )
318
340
  end
319
341
 
342
+ def authentication_token
343
+ username = Neo4jBolt.bolt_username
344
+ password = Neo4jBolt.bolt_password
345
+ return Neo4j::Driver::AuthTokens.none if username.nil? && password.nil?
346
+
347
+ if username.nil? || password.nil?
348
+ raise Error, "Neo4j username and password must either both be set or both be unset"
349
+ end
350
+
351
+ Neo4j::Driver::AuthTokens.basic(username, password)
352
+ end
353
+
320
354
  def leases
321
355
  @leases ||= 0
322
356
  end
@@ -332,8 +366,8 @@ module Neo4jBolt
332
366
  end
333
367
 
334
368
  class << self
335
- attr_reader :bolt_host, :bolt_port
336
- attr_accessor :bolt_verbosity
369
+ attr_reader :bolt_host, :bolt_port, :bolt_username, :bolt_password
370
+ attr_accessor :bolt_verbosity, :bolt_database
337
371
 
338
372
  def bolt_host=(host)
339
373
  reconfigure_driver if defined?(@bolt_host) && @bolt_host != host
@@ -345,6 +379,16 @@ module Neo4jBolt
345
379
  @bolt_port = port
346
380
  end
347
381
 
382
+ def bolt_username=(username)
383
+ reconfigure_driver if defined?(@bolt_username) && @bolt_username != username
384
+ @bolt_username = username
385
+ end
386
+
387
+ def bolt_password=(password)
388
+ reconfigure_driver if defined?(@bolt_password) && @bolt_password != password
389
+ @bolt_password = password
390
+ end
391
+
348
392
  private
349
393
 
350
394
  def reconfigure_driver
@@ -355,10 +399,23 @@ module Neo4jBolt
355
399
  self.bolt_host = "localhost"
356
400
  self.bolt_port = 7687
357
401
  self.bolt_verbosity = 0
402
+ self.bolt_username = ENV["NEO4J_USERNAME"]
403
+ self.bolt_password = ENV["NEO4J_PASSWORD"]
404
+ self.bolt_database = ENV["NEO4J_DATABASE"]
358
405
 
359
406
  # Keep both `include Neo4jBolt` and direct module-style calls working.
360
407
  extend self
361
408
 
409
+ def with_session(driver)
410
+ database = Neo4jBolt.bolt_database
411
+ if database.nil?
412
+ driver.session { |session| yield session }
413
+ else
414
+ driver.session(database: database) { |session| yield session }
415
+ end
416
+ end
417
+ private :with_session
418
+
362
419
  def neo4j_query(query, data = {}, &block)
363
420
  validate_parameters!(data)
364
421
  log_query(query, data)
@@ -368,7 +425,7 @@ module Neo4jBolt
368
425
  run_and_convert(context.transaction, query, data, &block)
369
426
  else
370
427
  DriverRegistry.with_driver do |driver|
371
- driver.session do |session|
428
+ with_session(driver) do |session|
372
429
  run_and_convert(session, query, data, &block)
373
430
  end
374
431
  end
@@ -408,7 +465,7 @@ module Neo4jBolt
408
465
  end
409
466
 
410
467
  DriverRegistry.with_driver do |driver|
411
- driver.session do |session|
468
+ with_session(driver) do |session|
412
469
  upstream_transaction = session.begin_transaction
413
470
  context = TransactionContext.new(
414
471
  session: session, transaction: upstream_transaction, depth: 1, rollback_only: false
@@ -514,8 +571,8 @@ module Neo4jBolt
514
571
  nil
515
572
  end
516
573
 
517
- def dump_database(io, progress_io: nil)
518
- progress = ProgressReporter.new(progress_io)
574
+ def dump_database(io, progress_io: nil, progress_color: "cyan")
575
+ progress = ProgressReporter.new(progress_io, color: progress_color)
519
576
  dumped_nodes = 0
520
577
  dumped_relationships = 0
521
578
 
@@ -574,7 +631,7 @@ module Neo4jBolt
574
631
  progress&.close
575
632
  end
576
633
 
577
- def load_database_dump(io, force_append: false, progress_io: nil,
634
+ def load_database_dump(io, force_append: false, progress_io: nil, progress_color: "cyan",
578
635
  initial_batch_size: LOAD_INITIAL_BATCH_SIZE)
579
636
  raise Error, "load_database_dump cannot run inside a transaction" if transaction_context
580
637
 
@@ -586,7 +643,7 @@ module Neo4jBolt
586
643
  raise Error, "There are nodes in this database, exiting now." unless count.zero?
587
644
  end
588
645
 
589
- progress = ProgressReporter.new(progress_io)
646
+ progress = ProgressReporter.new(progress_io, color: progress_color)
590
647
  node_batches = Hash.new { |hash, key| hash[key] = [] }
591
648
  relationship_batches = Hash.new { |hash, key| hash[key] = [] }
592
649
  total_nodes, total_relationships = parse_dump(io, node_batches, relationship_batches, progress)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: neo4j_bolt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Specht