ruby-kuzu 0.1.0 → 0.2.0

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: 992fc29928cf3ac28d7ef615302fafdbb65c18311bcac23ad7178ff56c2949f9
4
- data.tar.gz: 8ccd0253875842c9db1ef0b9de49d5a2120dd93193bcc19a840b020c8b3a8940
3
+ metadata.gz: ffa06875d65096bb5bac251440d1f1ec56d233a7675f86b9e4d22a5039d91020
4
+ data.tar.gz: 4666b63219b97e6fcb8781995bbb71bbb486a09f8bdbbe763e6f4b225101750a
5
5
  SHA512:
6
- metadata.gz: adce4adb10a022eb7ac6c079a66faaf91fb260e29a55f78b9ea235f3ecac3a6c2b068342e7274ac2bf3a8f6ba297ae494f19ba8e404c67d423f34976f0ce22d8
7
- data.tar.gz: 36b289d90d94cf0273745e89590be11ee62d005beac8f2b9fb7024e08414683bb510f1f158a81ced28ece50f082ba21d9efbb338795738a472c1397fc8045586
6
+ metadata.gz: fd651f4b5f1267a5e1725cc692ca3554842d58c4cb71bb63498172de7d6fcef6df3d7855bbdba2aadd2f35a01482e73104ef2a9647bf4a8221b3dba63e821096
7
+ data.tar.gz: b65eb14735f2d06883cc4392e67e4cb208fb415ac9de62a24d1e686bad95fa7fb0412124ef01908ce587a223bc1bcb37688d8cb4df10564f888acde3192171bd
checksums.yaml.gz.sig CHANGED
Binary file
data/History.md CHANGED
@@ -1,6 +1,23 @@
1
1
  # Release History for ruby-kuzu
2
2
 
3
3
  ---
4
+
5
+ ## v0.2.0 [2025-07-16] Michael Granger <ged@FaerieMUD.org>
6
+
7
+ Enhancements:
8
+
9
+ - Add support for the SERIAL type.
10
+ - Add Kuzu.is_database? method
11
+ - Flesh out documentation
12
+
13
+ Bugfixes:
14
+
15
+ - Fixups for Linux and Kuzu 0.11
16
+ - Fix storage version check (0.11+)
17
+ - Fix a bug in Result#next
18
+ - Fix Date type conversion
19
+
20
+
4
21
  ## v0.1.0 [2025-06-17] Michael Granger <ged@FaerieMUD.org>
5
22
 
6
23
  Enhancements:
data/README.md CHANGED
@@ -47,15 +47,129 @@ Once you have a Kuzu::Database object, you need a connection to actually use it:
47
47
  # => #<Kuzu::Connection:0x0000000122a41f28 threads:16>
48
48
 
49
49
 
50
-
51
50
  ### Querying
52
51
 
52
+ There are two ways of running queries, immediate execution and via a prepared
53
+ statement. Either method returns one or more results as Kuzu::Result objects.
54
+
55
+
53
56
  ### Results
54
57
 
58
+ The each result of a query in a query string is returned as a Kuzu::Result. If
59
+ there are more than one queries in the query string, the Results will be
60
+ chained together so they can be iterated over.
61
+
62
+ Each tuple in a Result can be fetched using Kuzu::Result#next, which returns a
63
+ Hash of the variables in the `RETURN` clause, keyed by the column name as a
64
+ `String`:
65
+
66
+ res = conn.query( 'MATCH (a:User)-[f:Follows]->(b:User) RETURN a.name, b.name, f.since;' )
67
+ # => #<Kuzu::Result:0x00000001268449d8 success: true (4 tuples of 3 columns)>
68
+ res.next
69
+ # => {"a.name" => "Adam", "b.name" => "Karissa", "f.since" => 2020}
70
+ res.next
71
+ # => {"a.name" => "Adam", "b.name" => "Zhang", "f.since" => 2020}
72
+
73
+ The value's type will be determined by the Kuzu datatype of that column of the
74
+ result. See Kuzu::Result for a mapping of the types.
75
+
76
+ If there are more tuples to fetch, Kuzu::Result#has_next? will return `true`:
77
+
78
+ res.has_next?
79
+ # => true
80
+ res.next
81
+ # => {"a.name" => "Karissa", "b.name" => "Zhang", "f.since" => 2021}
82
+ res.next
83
+ # => {"a.name" => "Zhang", "b.name" => "Noura", "f.since" => 2022}
84
+ res.has_next?
85
+ # => false
86
+ res.next
87
+ # => nil
88
+
89
+ Kuzu::Result is also Enumerable, so you can `#each` over its tuples:
90
+
91
+ res.each
92
+ # => #<Enumerator: ...>
93
+ res.map do |tuple|
94
+ "%s has followed %s since %s" % tuple.values_at('a.name', 'b.name', 'f.since')
95
+ end
96
+ # => ["Adam has followed Karissa since 2020",
97
+ # "Adam has followed Zhang since 2020",
98
+ # "Karissa has followed Zhang since 2021",
99
+ # "Zhang has followed Noura since 2022"]
100
+
101
+ If the query string has more than one query in it, a separate Kuzu::Result is
102
+ linked to the previous one, and can be fetched using Kuzu::Result#next_set.
103
+
104
+ For example:
105
+
106
+ res = conn.query( <<~END_OF_QUERY )
107
+ MATCH (a:User)-[f:Follows]->(b:User)
108
+ RETURN a.name, b.name, f.since;
109
+ MATCH (u:User) RETURN *
110
+ END_OF_QUERY
111
+ # => #<Kuzu::Result:0x000000011e6faaf8 success: true (4 tuples of 3 columns)>
112
+ res.to_a
113
+ # => [{"a.name" => "Adam", "b.name" => "Karissa", "f.since" => 2020},
114
+ # {"a.name" => "Adam", "b.name" => "Zhang", "f.since" => 2020},
115
+ # {"a.name" => "Karissa", "b.name" => "Zhang", "f.since" => 2021},
116
+ # {"a.name" => "Zhang", "b.name" => "Noura", "f.since" => 2022}]
117
+ res.has_next_set?
118
+ # => true
119
+ res2 = res.next_set
120
+ # => #<Kuzu::Result:0x000000011e338fa0 success: true (4 tuples of 1 columns)>
121
+ res2.to_a
122
+ # => [{"u" => #<Kuzu::Node:0x000000011e7553b8 @id=[0, 0], @label="User",
123
+ # @properties={name: "Adam", age: 30}>},
124
+ # {"u" => #<Kuzu::Node:0x000000011e7551b0 @id=[0, 1], @label="User",
125
+ # @properties={name: "Karissa", age: 40}>},
126
+ # {"u" => #<Kuzu::Node:0x000000011e755048 @id=[0, 2], @label="User",
127
+ # @properties={name: "Zhang", age: 50}>},
128
+ # {"u" => #<Kuzu::Node:0x000000011e754ee0 @id=[0, 3], @label="User",
129
+ # @properties={name: "Noura", age: 25}>}]
130
+ res2.has_next_set?
131
+ # => false
132
+ res2.next_set
133
+ # => nil
134
+
135
+
55
136
  ### Prepared Statements
56
137
 
57
- ### Datatypes
138
+ An alternative to using strings to query the database is to used *prepared statements*. These have a number of advantages, such as reusability and using parameters for queries instead of string interpolation.
139
+
140
+ You can create a Kuzu::PreparedStatement by calling Kuzu::Connection#prepare, then execute it one or more times with parameters using Kuzu::PreparedStatement#execute:
141
+
142
+ stmt = conn.prepare( <<~END_OF_QUERY )
143
+ MATCH (a:User)-[f:Follows]->(b:User)
144
+ WHERE a.name = $name
145
+ RETURN a.name, b.name, f.since'
146
+ END_OF_QUERY
147
+ # => #<Kuzu::PreparedStatement:0x000000011e919b68>
148
+ res = stmt.execute( name: "Karissa" )
149
+ # => #<Kuzu::Result:0x000000011ee8df90 success: true (1 tuples of 3 columns)>
150
+ res.to_a
151
+ # => [{"a.name" => "Karissa", "b.name" => "Zhang", "f.since" => 2021}]
152
+
58
153
 
154
+ ### Result Memory Management
155
+
156
+ Because of the way Ruby frees memory when it's shutting down (i.e., the order is indeterminate), Kuzu::Result objects may not be freed immediately when they go out of scope. To provide some way to manage this, the Kuzu::Result#finish call is provided as a way to explicitly destroy the underlying Kuzu data structure so the Result can be freed. Since this is somewhat inconvenient to manage, there are two forms of Kuzu::Connection#query and Kuzu::PreparedStatement#execute, one which returns a Result and a "bang" equivalent one which just returns success or failure. Additionally, passing a block to either method will yield the Result to the block and then immediately `finish` the Result for you and return the block's value.
157
+
158
+ query_string = 'MATCH (a:User)-[f:Follows]->(b:User) RETURN a.name, b.name, f.since'
159
+ conn.query!( query_string )
160
+ # => true
161
+ conn.query( query_string ) {|res| res.tuples }
162
+ # => [{"a.name" => "Adam", "b.name" => "Karissa", "f.since" => 2020},
163
+ # {"a.name" => "Adam", "b.name" => "Zhang", "f.since" => 2020},
164
+ # {"a.name" => "Karissa", "b.name" => "Zhang", "f.since" => 2021},
165
+ # {"a.name" => "Zhang", "b.name" => "Noura", "f.since" => 2022}]
166
+
167
+ stmt = conn.prepare( "CREATE (:User {name: $name, age: $age})" )
168
+ # => #<Kuzu::PreparedStatement:0x000000010bc7cfe8>
169
+ stmt.execute!( name: 'David', age: 19 )
170
+ # => true
171
+ stmt.execute!( name: 'Agnes', age: 28 )
172
+ # => true
59
173
 
60
174
 
61
175
  ## Examples
@@ -64,26 +178,36 @@ Once you have a Kuzu::Database object, you need a connection to actually use it:
64
178
 
65
179
  db = Kuzu.database
66
180
  conn = db.connect
67
- conn.query("CREATE NODE TABLE User(name STRING, age INT64, PRIMARY KEY (name))")
68
- conn.query("CREATE NODE TABLE City(name STRING, population INT64, PRIMARY KEY (name))")
69
- conn.query("CREATE REL TABLE Follows(FROM User TO User, since INT64)")
70
- conn.query("CREATE REL TABLE LivesIn(FROM User TO City)")
181
+ conn.run("CREATE NODE TABLE User(name STRING, age INT64, PRIMARY KEY (name))")
182
+ conn.run("CREATE NODE TABLE City(name STRING, population INT64, PRIMARY KEY (name))")
183
+ conn.run("CREATE REL TABLE Follows(FROM User TO User, since INT64)")
184
+ conn.run("CREATE REL TABLE LivesIn(FROM User TO City)")
71
185
 
72
186
  # Load data.
73
- conn.query("COPY User FROM \"user.csv\"")
74
- conn.query("COPY City FROM \"city.csv\"")
75
- conn.query("COPY Follows FROM \"follows.csv\"")
76
- conn.query("COPY LivesIn FROM \"lives-in.csv\"")
187
+ conn.run("COPY User FROM \"user.csv\"")
188
+ conn.run("COPY City FROM \"city.csv\"")
189
+ conn.run("COPY Follows FROM \"follows.csv\"")
190
+ conn.run("COPY LivesIn FROM \"lives-in.csv\"")
77
191
 
78
192
  # Execute a simple query.
79
193
  result = conn.query("MATCH (a:User)-[f:Follows]->(b:User) RETURN a.name, f.since, b.name;")
80
194
 
81
195
  # Output query result.
82
196
  result.each do |tuple|
83
- name, since, name2 = tuple.values_at( :a_name, :f_since, :b_name )
197
+ name, since, name2 = tuple.values_at( 'a.name', 'f.since', 'b.name' )
84
198
  puts "%s follows %s since %lld", [ name, name2, since ]
85
199
  end
86
200
 
201
+ result.finish
202
+
203
+
204
+ ## To-Do List
205
+
206
+ - `UNION` result type.
207
+ - `JSON` result type from the JSON extension
208
+ - Better memory management for Kuzu::Results
209
+
210
+
87
211
  ## Requirements
88
212
 
89
213
  - Ruby >= 3
@@ -28,8 +28,10 @@
28
28
  #ifdef HAVE_STDARG_PROTOTYPES
29
29
  #include <stdarg.h>
30
30
  #define va_init_list(a, b) va_start (a, b)
31
- void rkuzu_log_obj (VALUE, const char *, const char *, ...);
32
- void rkuzu_log (const char *, const char *, ...);
31
+ void rkuzu_log_obj (VALUE, const char *, const char *, ...)
32
+ __attribute__ ((format (printf, 2, 0)));
33
+ void rkuzu_log (const char *, const char *, ...)
34
+ __attribute__ ((format (printf, 1, 0)));
33
35
  #else
34
36
  #include <varargs.h>
35
37
  #define va_init_list(a, b) va_start (a)
@@ -43,7 +43,7 @@ rkuzu_get_prepared_statement( VALUE prepared_statement_obj )
43
43
  * Allocation function
44
44
  */
45
45
  static rkuzu_prepared_statement *
46
- rkuzu_prepared_statement_alloc()
46
+ rkuzu_prepared_statement_alloc( void )
47
47
  {
48
48
  rkuzu_prepared_statement *ptr = ALLOC( rkuzu_prepared_statement );
49
49
 
@@ -51,7 +51,7 @@ rkuzu_query_summary_s_allocate( VALUE klass )
51
51
  * call-seq:
52
52
  * Kuzu::QuerySummary.from_result( result ) -> query_summary
53
53
  *
54
- * Return a Kuzu::QuerySummary from a Kuzu::QueryResult.
54
+ * Return a Kuzu::QuerySummary from a Kuzu::Result.
55
55
  *
56
56
  */
57
57
  static VALUE
@@ -408,7 +408,7 @@ rkuzu_result_get_column_names( VALUE self )
408
408
 
409
409
  for ( uint64_t i = 0 ; i < col_count ; i++ ) {
410
410
  if ( kuzu_query_result_get_column_name(&result->result, i, &name) != KuzuSuccess ) {
411
- rb_raise( rkuzu_eError, "couldn't fetch name of column %llu", i );
411
+ rb_raise( rkuzu_eError, "couldn't fetch name of column %lu", i );
412
412
  }
413
413
  rb_ary_push( rval, rb_str_new2(name) );
414
414
  }
data/ext/kuzu_ext/types.c CHANGED
@@ -165,8 +165,8 @@ rkuzu_convert_date( kuzu_value *value )
165
165
 
166
166
  kuzu_date_to_tm( typed_value, &time );
167
167
 
168
- argv[0] = INT2FIX( time.tm_year );
169
- argv[1] = INT2FIX( time.tm_mon );
168
+ argv[0] = INT2FIX( time.tm_year + 1900 );
169
+ argv[1] = INT2FIX( time.tm_mon + 1 );
170
170
  argv[2] = INT2FIX( time.tm_mday );
171
171
 
172
172
  return rb_class_new_instance( 3, argv, rkuzu_rb_cDate );
@@ -554,6 +554,9 @@ rkuzu_convert_kuzu_value_to_ruby( kuzu_data_type_id type_id, kuzu_value *value )
554
554
  case KUZU_UINT8: return rkuzu_convert_uint8( value );
555
555
  case KUZU_INT128: return rkuzu_convert_int128( value );
556
556
 
557
+ // Serials just come out as int64s
558
+ case KUZU_SERIAL: return rkuzu_convert_int64( value );
559
+
557
560
  case KUZU_DOUBLE: return rkuzu_convert_double( value );
558
561
  case KUZU_FLOAT: return rkuzu_convert_float( value );
559
562
 
data/lib/kuzu/result.rb CHANGED
@@ -6,6 +6,47 @@ require 'kuzu' unless defined?( Kuzu )
6
6
 
7
7
 
8
8
  # Kùzu query result class
9
+ #
10
+ # These objects contain one result set from either a Kuzu::Connection#query call
11
+ # or Kuzu::PreparedStatement#execute. If there are multiple result sets, you can
12
+ # fetch the next one by calling Kuzu::Result#next_set. You can use #has_next_set?
13
+ # to test for a following set.
14
+ #
15
+ # Tuple values are converted to corresponding Ruby objects:
16
+ #
17
+ # | Kuzu Type | Ruby Type |
18
+ # | --------------- | ----------------------------------------------- |
19
+ # | +INT8+ | +Integer+ |
20
+ # | +INT16+ | +Integer+ |
21
+ # | +INT32+ | +Integer+ |
22
+ # | +INT64+ | +Integer+ |
23
+ # | +INT128+ | +Integer+ |
24
+ # | +UINT8+ | +Integer+ |
25
+ # | +UINT16+ | +Integer+ |
26
+ # | +UINT32+ | +Integer+ |
27
+ # | +UINT64+ | +Integer+ |
28
+ # | +FLOAT+ | +Float+ |
29
+ # | +DOUBLE+ | +Float+ |
30
+ # | +DECIMAL+ | +Float+ |
31
+ # | +BOOLEAN+ | +TrueClass+ or +FalseClass+ |
32
+ # | +UUID+ | +String+ (UTF-8 encoding) |
33
+ # | +STRING+ | +String+ (UTF-8 encoding) |
34
+ # | +NULL+ | +NilClass+ |
35
+ # | +DATE+ | +Date+ |
36
+ # | +TIMESTAMP+ | +Time+ |
37
+ # | +INTERVAL+ | +Float+ (interval in seconds) |
38
+ # | +STRUCT+ | +OpenStruct+ via the +ostruct+ standard library |
39
+ # | +MAP+ | +Hash+ |
40
+ # | +UNION+ | (not yet handled) |
41
+ # | +BLOB+ | +String+ (+ASCII_8BIT+ encoding) |
42
+ # | +SERIAL+ | +Integer+ |
43
+ # | +NODE+ | Kuzu::Node |
44
+ # | +REL+ | Kuzu::Rel |
45
+ # | +RECURSIVE_REL+ | Kuzu::RecursiveRel |
46
+ # | +LIST+ | +Array+ |
47
+ # | +ARRAY+ | +Array+ |
48
+ #
49
+ #
9
50
  class Kuzu::Result
10
51
  extend Loggability
11
52
 
@@ -60,7 +101,8 @@ class Kuzu::Result
60
101
 
61
102
  ### Get the next tuple of the result as a Hash.
62
103
  def next
63
- pairs = self.column_names.zip( self.get_next_values )
104
+ values = self.get_next_values or return nil
105
+ pairs = self.column_names.zip( values )
64
106
  return Hash[ pairs ]
65
107
  end
66
108
 
@@ -110,11 +152,10 @@ class Kuzu::Result
110
152
  if self.finished?
111
153
  details = " (finished)"
112
154
  else
113
- details = " success: %p (%d tuples of %d columns): %s" % [
155
+ details = " success: %p (%d tuples of %d columns)" % [
114
156
  self.success?,
115
157
  self.num_tuples,
116
158
  self.num_columns,
117
- self.to_s,
118
159
  ]
119
160
  end
120
161
 
data/lib/kuzu.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # -*- ruby -*-
2
2
 
3
+ require 'pathname'
3
4
  require 'loggability'
4
5
 
5
6
  require_relative 'kuzu_ext'
@@ -11,7 +12,10 @@ module Kuzu
11
12
 
12
13
 
13
14
  # Library version
14
- VERSION = '0.1.0'
15
+ VERSION = '0.2.0'
16
+
17
+ # Name of the file to look for when testing a path to see if it's a Kuzu database.
18
+ KUZU_CATALOG_FILENAME = 'data.kz'
15
19
 
16
20
 
17
21
  # Set up a logger for Kuzu classes
@@ -52,6 +56,23 @@ module Kuzu
52
56
  end
53
57
 
54
58
 
59
+ ### Returns +true+ if the specified +pathname+ appears to be a valid Kuzu database
60
+ ### for the current version of the storage format.
61
+ def self::is_database?( pathname )
62
+ pathname = Pathname( pathname )
63
+ if Kuzu.storage_version <= 38
64
+ return false unless pathname.directory?
65
+ testfile = pathname / KUZU_CATALOG_FILENAME
66
+ return testfile.exist?
67
+ else
68
+ return false unless pathname.file?
69
+ magic = pathname.read( 5 )
70
+ return magic[0, 4] == 'KUZU' && magic[4, 1].ord == Kuzu.storage_version
71
+ end
72
+ end
73
+ singleton_class.alias_method( :is_kuzu_database?, :is_database? )
74
+
75
+
55
76
  ### Return a Time object from the given +milliseconds+ epoch time.
56
77
  def self::timestamp_from_timestamp_ms( milliseconds )
57
78
  seconds, subsec = milliseconds.divmod( 1_000 )
@@ -74,4 +95,3 @@ module Kuzu
74
95
  end
75
96
 
76
97
  end # module Kuzu
77
-
@@ -8,12 +8,18 @@ require 'kuzu/database'
8
8
  RSpec.describe( Kuzu::Database ) do
9
9
 
10
10
  let( :spec_tmpdir ) do
11
- tmpfile_pathname()
11
+ path = tmpfile_pathname()
12
+ path.mkpath
13
+ return path
12
14
  end
13
15
 
14
16
  let( :db_path ) { spec_tmpdir + 'spec_db' }
15
17
 
16
18
 
19
+ after( :each ) do
20
+ GC.start
21
+ end
22
+
17
23
  it "can be created in-memory" do
18
24
  instance = described_class.new( '' )
19
25
  expect( instance ).to be_a( described_class )
@@ -89,6 +89,22 @@ RSpec.describe( Kuzu::Result ) do
89
89
  end
90
90
 
91
91
 
92
+ it "handles a #next after it finishes iteration over the current set" do
93
+ setup_demo_db()
94
+
95
+ result = described_class.from_query( connection, <<~END_OF_QUERY )
96
+ MATCH ( a:User )-[ f:Follows ]->( b:User )
97
+ RETURN a.name, b.name, f.since;
98
+ END_OF_QUERY
99
+
100
+ result.tuples # Iterate over all tuples
101
+
102
+ expect( result.next ).to be_nil
103
+
104
+ result.finish
105
+ end
106
+
107
+
92
108
  it "can fetch individual result tuples via the index operator" do
93
109
  setup_demo_db()
94
110
 
@@ -34,6 +34,25 @@ RSpec.describe( "data types" ) do
34
34
  end
35
35
 
36
36
 
37
+ it "coverts DATE values to Date objects" do
38
+ result = connection.query( %{RETURN CAST('2025-07-14', 'DATE') as x;} )
39
+
40
+ expect( result ).to be_a( Kuzu::Result )
41
+ expect( result ).to be_success
42
+
43
+ value = result.first
44
+ expect( value ).to include( 'x' )
45
+
46
+ x = value['x']
47
+ expect( x ).to be_a( Date )
48
+ expect( x.year ).to eq( 2025 )
49
+ expect( x.month ).to eq( 7 )
50
+ expect( x.day ).to eq( 14 )
51
+
52
+ result.finish
53
+ end
54
+
55
+
37
56
  it "converts STRUCT values to OpenStructs" do
38
57
  result = connection.query( "RETURN {first: 'Adam', last: 'Smith'} AS record;" )
39
58
 
@@ -251,4 +270,15 @@ RSpec.describe( "data types" ) do
251
270
  end
252
271
 
253
272
 
273
+ it "converts SERIAL types to Integer objects" do
274
+ result = connection.query( %{RETURN CAST(133, "SERIAL") AS s;} )
275
+ rval = result.first['s']
276
+
277
+ expect( rval ).to be_an( Integer )
278
+ expect( rval ).to eq( 133 )
279
+
280
+ result.finish
281
+ end
282
+
283
+
254
284
  end
data/spec/kuzu_spec.rb CHANGED
@@ -7,7 +7,9 @@ require 'kuzu'
7
7
  RSpec.describe( Kuzu ) do
8
8
 
9
9
  let( :spec_tmpdir ) do
10
- tmpfile_pathname()
10
+ path = tmpfile_pathname()
11
+ path.mkpath
12
+ return path
11
13
  end
12
14
 
13
15
 
@@ -49,7 +51,33 @@ RSpec.describe( Kuzu ) do
49
51
  result = described_class.database( filename )
50
52
 
51
53
  expect( result ).to be_a( Kuzu::Database )
52
- expect( filename ).to be_a_directory
54
+ if Kuzu.storage_version <= 38
55
+ expect( filename ).to be_a_directory
56
+ else
57
+ expect( filename ).to be_a_file
58
+ end
59
+ end
60
+
61
+
62
+ it "can tell whether a string looks like a path to a Kuzu database" do
63
+ path = spec_tmpdir + 'spec_db'
64
+
65
+ expect {
66
+ described_class.database( path )
67
+ }.to change {
68
+ described_class.is_database?( path.to_s )
69
+ }.from( false ).to( true )
70
+ end
71
+
72
+
73
+ it "can tell whether a Pathname looks like a path to a Kuzu database" do
74
+ path = spec_tmpdir + 'spec_db'
75
+
76
+ expect {
77
+ described_class.database( path )
78
+ }.to change {
79
+ described_class.is_database?( path )
80
+ }.from( false ).to( true )
53
81
  end
54
82
 
55
83
  end
data/spec/spec_helper.rb CHANGED
@@ -54,7 +54,7 @@ module Kuzu::SpecHelpers
54
54
 
55
55
  ### Return a Pathname pointing to a temporary file.
56
56
  def tmpfile_pathname( filetype='spec' )
57
- Pathname(Dir::Tmpname.create(['kuzu-', '-test-' + filetype]) {})
57
+ return Pathname( Dir::Tmpname.create(['kuzu-', '-test-' + filetype]) {} )
58
58
  end
59
59
 
60
60
 
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-kuzu
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Granger
@@ -34,7 +34,7 @@ cert_chain:
34
34
  8qAqdfV+4u6Huu1KzAuDQCheyEyISsLST37sU/irV3czV6BiFipWag1XiJciRT3A
35
35
  wZqCfTNVHTdtsCbfdA1DsA3RdG2iEH3TOHzv1Rqzqh4=
36
36
  -----END CERTIFICATE-----
37
- date: 2025-06-17 00:00:00.000000000 Z
37
+ date: 2025-07-16 00:00:00.000000000 Z
38
38
  dependencies:
39
39
  - !ruby/object:Gem::Dependency
40
40
  name: rake-compiler
@@ -158,7 +158,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
158
158
  - !ruby/object:Gem::Version
159
159
  version: '0'
160
160
  requirements: []
161
- rubygems_version: 3.6.7
161
+ rubygems_version: 3.6.9
162
162
  specification_version: 4
163
163
  summary: A Ruby binding for the Kùzu embedded graph database.
164
164
  test_files: []
metadata.gz.sig CHANGED
@@ -1,2 +1,3 @@
1
- �x02ڶ�X?�' ��]Ԛ;�h'���L�%�qoq;��(C~���f�US��RF-h�Ϛl�R�.L`�c�R�E��:�~nj�8c��Q�K6�����r�𿿫��2ۍ_E�kȼm�#j�-H#]��t�����/#ȩ~L(K}�Y��8T��<A���?���m�
2
- $��c;�T:5�f%b�T In,۳`W~F~`j�H8Y��߉��#@qſ�,`�n���Ԅ�r�j.e�N&}!�f�� ��
1
+ J�DZ#\A3��K���&��E����Q���dEcߋM 9��8S�����Y��"_���'����N����A'8SV 8��(�u��y�}bO �ҹG�ņ���C˽h؇�4��S \�.�)9
2
+ ���{��~86�#+�tu����R�����_�}x��<gx8�;)a����U��%��
3
+ |��r�7˫�<}nVt �ml�SN�%Ѳ��4������j��F���n^M���������ZG��0N� �D� �󔹰���N���8�ϔU��u�����P�g�q��qf�h�C��c�?2�A�owe�#�.