mongory 0.7.7-x86_64-darwin → 0.8.0-x86_64-darwin
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 +4 -4
- data/README.md +33 -50
- data/docs/clang_bridge.md +24 -0
- data/examples/benchmark.rb +5 -19
- data/ext/mongory_ext/extconf.rb +19 -0
- data/ext/mongory_ext/mongory_ext.c +196 -162
- data/lib/core/2.6/mongory_ext.bundle +0 -0
- data/lib/core/2.7/mongory_ext.bundle +0 -0
- data/lib/core/3.0/mongory_ext.bundle +0 -0
- data/lib/core/3.1/mongory_ext.bundle +0 -0
- data/lib/core/3.2/mongory_ext.bundle +0 -0
- data/lib/core/3.3/mongory_ext.bundle +0 -0
- data/lib/core/3.4/mongory_ext.bundle +0 -0
- data/lib/mongory/c_matcher.rb +57 -0
- data/lib/mongory/query_builder.rb +17 -0
- data/lib/mongory/version.rb +1 -1
- data/lib/mongory.rb +3 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a78d545b33e4f3c3edbbcda2bb6753a901eeec085dbd56b38818eae8f00ddc2
|
4
|
+
data.tar.gz: 1d66943eac4d37e23ffed4383ca62fea7cf96445ffecd2b6fa7f1e17aaf62129
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 50f4a390177a2deaa33b5abc510148025a34ff025e232e6d70ecfa504501e98ec6845e209828fc11e8b7a37de681cff9f96096f463fad118e9c9315ba666800f
|
7
|
+
data.tar.gz: a2a782689e19dbc81e6793d034b8eb00fcbfa7a83f593d47e87ec8691b24ef978946fb14187306d829ed386263cd04b053377b89007d502c153054ca343c62d6
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
# Mongory
|
1
|
+
# Mongory
|
2
|
+
### Let you query everywhere !
|
2
3
|
|
3
|
-
|
4
|
+
This is a Mongo-like in-memory query DSL for Ruby.
|
4
5
|
|
5
6
|
Mongory lets you filter and query in-memory collections using syntax and semantics similar to MongoDB. It is designed for expressive chaining, symbolic operators, and composable matchers.
|
6
7
|
|
@@ -53,7 +54,18 @@ gem 'mongory'
|
|
53
54
|
|
54
55
|
#### Before installing: install build tools
|
55
56
|
|
56
|
-
Mongory ships with an optional native
|
57
|
+
Mongory ships with an optional native C extension.
|
58
|
+
Before installing Mongory, make sure these following precompiled package support your platform:
|
59
|
+
- x86_64-linux-musl (Linux)
|
60
|
+
- aarch64-linux-musl (Linux)
|
61
|
+
- x86_64-linux (Linux)
|
62
|
+
- aarch64-linux (Linux)
|
63
|
+
- x86_64-darwin (MacOS Intel)
|
64
|
+
- arm64-darwin (MacOS Apple Silicon)
|
65
|
+
- x64-mingw32 (Windows)
|
66
|
+
- x64-mingw-ucrt (Windows)
|
67
|
+
|
68
|
+
If your platform excludes, make sure your system has a C build toolchain (gcc/clang and make). Install the toolchain with the following commands for your platform:
|
57
69
|
|
58
70
|
- Debian/Ubuntu (including ruby:*-slim base images)
|
59
71
|
```bash
|
@@ -85,19 +97,6 @@ yum groupinstall -y "Development Tools"
|
|
85
97
|
xcode-select --install
|
86
98
|
```
|
87
99
|
|
88
|
-
#### Rails Generator
|
89
|
-
|
90
|
-
You can install a starter configuration with:
|
91
|
-
|
92
|
-
```bash
|
93
|
-
rails g mongory:install
|
94
|
-
```
|
95
|
-
|
96
|
-
This will generate `config/initializers/mongory.rb` and set up:
|
97
|
-
- Optional symbol operator snippets (e.g. `:age.gt => 18`)
|
98
|
-
- Class registration (e.g. `Array`, `ActiveRecord::Relation`, etc.)
|
99
|
-
- Custom value/key converters for your ORM
|
100
|
-
|
101
100
|
### Basic Usage
|
102
101
|
```ruby
|
103
102
|
records = [
|
@@ -119,29 +118,18 @@ limited = records.mongory
|
|
119
118
|
.where(:age.gte => 18) # Conditions apply to limited set
|
120
119
|
```
|
121
120
|
|
122
|
-
|
123
|
-
|
124
|
-
Mongory-rb includes an optional high-performance C extension powered by [mongory-core](https://github.com/mongoryhq/mongory-core):
|
121
|
+
#### Rails Generator
|
125
122
|
|
126
|
-
|
127
|
-
- C99-compatible compiler (gcc/clang)
|
128
|
-
- CMake >= 3.12 (optional; only needed if you want to build `mongory-core` standalone or run its native tests)
|
123
|
+
You can install a starter configuration with:
|
129
124
|
|
130
|
-
**Installation:**
|
131
125
|
```bash
|
132
|
-
|
133
|
-
brew install cmake
|
134
|
-
|
135
|
-
# Ubuntu/Debian
|
136
|
-
sudo apt install cmake build-essential
|
137
|
-
|
138
|
-
# CentOS/RHEL
|
139
|
-
sudo yum install cmake gcc make
|
126
|
+
rails g mongory:install
|
140
127
|
```
|
141
128
|
|
142
|
-
|
143
|
-
|
144
|
-
|
129
|
+
This will generate `config/initializers/mongory.rb` and set up:
|
130
|
+
- Optional symbol operator snippets (e.g. `:age.gt => 18`)
|
131
|
+
- Class registration (e.g. `Array`, `ActiveRecord::Relation`, etc.)
|
132
|
+
- Custom value/key converters for your ORM
|
145
133
|
|
146
134
|
## Positioning
|
147
135
|
|
@@ -201,8 +189,8 @@ This will:
|
|
201
189
|
|
202
190
|
The generated matcher will:
|
203
191
|
- Be named `ClassInMatcher`
|
204
|
-
- Register the operator as `$classIn`
|
205
|
-
-
|
192
|
+
- Register the operator as `$classIn` makes it available in queries
|
193
|
+
- Enable `:field.class_in` query snippet (If symbol snippet enabled)
|
206
194
|
|
207
195
|
Example usage of the generated matcher:
|
208
196
|
```ruby
|
@@ -248,8 +236,7 @@ User.where(status: 'active').mongory.where(:age.gte => 18, :name.regex => "^S.+"
|
|
248
236
|
```
|
249
237
|
|
250
238
|
This injects a `.mongory` method via an internal extension module.
|
251
|
-
|
252
|
-
Internally, the query is compiled into a matcher tree using the `QueryMatcher` and `ConditionConverter`.
|
239
|
+
Internally, the query is compiled into a matcher tree.
|
253
240
|
|
254
241
|
| Method | Description | Example |
|
255
242
|
|--------|-------------|---------|
|
@@ -311,19 +298,13 @@ And: {"age"=>{"$gte"=>18}, "$or"=>[{"status"=>"active"}, {"name"=>{"$regex"=>/^J
|
|
311
298
|
|
312
299
|
This helps you understand how your query is being processed and can be useful for debugging complex conditions.
|
313
300
|
|
314
|
-
Or use
|
301
|
+
Or use trace for detailed matching process:
|
315
302
|
```ruby
|
316
|
-
# Enable debugging
|
317
|
-
Mongory.debugger.enable
|
318
|
-
|
319
303
|
# Execute your query
|
320
304
|
query = Mongory.build_query(users).where(age: { :$gt => 18 })
|
321
|
-
query.
|
305
|
+
query.trace do |user|
|
322
306
|
puts user
|
323
307
|
end
|
324
|
-
|
325
|
-
# Display the debug trace
|
326
|
-
Mongory.debugger.display
|
327
308
|
```
|
328
309
|
|
329
310
|
The debug output will show detailed matching process with full class names:
|
@@ -351,6 +332,7 @@ The debug output includes:
|
|
351
332
|
| Pattern | `$regex` |
|
352
333
|
| Presence | `$exists`, `$present` |
|
353
334
|
| Nested Match | `$elemMatch`, `$every` |
|
335
|
+
| Other | `$size` |
|
354
336
|
|
355
337
|
Note: Some operators are Mongory-specific and not available in MongoDB:
|
356
338
|
- `$present`: Checks if a field is considered "present" (not nil, not empty, not KEY_NOT_FOUND)
|
@@ -399,10 +381,11 @@ end
|
|
399
381
|
|
400
382
|
1. **Debugging Queries**
|
401
383
|
```ruby
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
384
|
+
records.mongory.where(:age => 18).trace.to_a
|
385
|
+
```
|
386
|
+
If using C extension:
|
387
|
+
```ruby
|
388
|
+
records.mongory.c.where(:age => 18).trace.to_a
|
406
389
|
```
|
407
390
|
|
408
391
|
2. **Common Issues**
|
data/docs/clang_bridge.md
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
### C Extension (Optional but Recommended)
|
2
|
+
|
3
|
+
Mongory-rb includes an optional high-performance C extension powered by [mongory-core](https://github.com/mongoryhq/mongory-core):
|
4
|
+
|
5
|
+
**System Dependencies:**
|
6
|
+
- C99-compatible compiler (gcc/clang)
|
7
|
+
- CMake >= 3.12 (optional; only needed if you want to build `mongory-core` standalone or run its native tests)
|
8
|
+
|
9
|
+
**Installation:**
|
10
|
+
```bash
|
11
|
+
# macOS
|
12
|
+
brew install cmake
|
13
|
+
|
14
|
+
# Ubuntu/Debian
|
15
|
+
sudo apt install cmake build-essential
|
16
|
+
|
17
|
+
# CentOS/RHEL
|
18
|
+
sudo yum install cmake gcc make
|
19
|
+
```
|
20
|
+
|
21
|
+
The C extension provides significant performance improvements for large datasets. If not available, Mongory-rb automatically falls back to pure Ruby implementation.
|
22
|
+
|
23
|
+
Note: The Ruby C extension is built via Ruby's `mkmf` (see `ext/mongory_ext/extconf.rb`) and compiles `mongory-core` sources directly. You do not need CMake for normal gem installation.
|
24
|
+
|
1
25
|
# Clang Bridge (C Extension)
|
2
26
|
|
3
27
|
The Clang bridge connects the Ruby DSL to the `mongory-core` engine via a compact C layer. It exposes two key entry points:
|
data/examples/benchmark.rb
CHANGED
@@ -49,7 +49,7 @@ end
|
|
49
49
|
# Simple query (Mongory) test
|
50
50
|
puts "\nSimple query (Mongory) (#{size} records):"
|
51
51
|
gc_handler do
|
52
|
-
builder = records.mongory.where(:age.gte => 18)
|
52
|
+
builder = records.mongory.where({:age.gte => 18})
|
53
53
|
5.times do
|
54
54
|
result = Benchmark.measure do
|
55
55
|
builder.to_a
|
@@ -62,7 +62,7 @@ end
|
|
62
62
|
# Simple query (Mongory::CMatcher) test
|
63
63
|
puts "\nSimple query (Mongory::CMatcher) (#{size} records):"
|
64
64
|
gc_handler do
|
65
|
-
matcher = Mongory::CMatcher.new(:age.gte => 18)
|
65
|
+
matcher = Mongory::CMatcher.new({:age.gte => 18})
|
66
66
|
5.times do
|
67
67
|
result = Benchmark.measure do
|
68
68
|
records.select { |r| matcher.match?(r) }
|
@@ -75,7 +75,7 @@ end
|
|
75
75
|
# Simple query (Mongory::CQueryBuilder) test
|
76
76
|
puts "\nSimple query (Mongory::CQueryBuilder) (#{size} records):"
|
77
77
|
gc_handler do
|
78
|
-
builder = Mongory::CQueryBuilder.new(records).where(:age.gte => 18)
|
78
|
+
builder = Mongory::CQueryBuilder.new(records).where({:age.gte => 18})
|
79
79
|
5.times do
|
80
80
|
result = Benchmark.measure do
|
81
81
|
builder.to_a
|
@@ -138,12 +138,12 @@ end
|
|
138
138
|
|
139
139
|
puts "\nComplex query (Mongory) use CMatcher (#{size} records):"
|
140
140
|
gc_handler do
|
141
|
-
matcher = Mongory::CMatcher.new(
|
141
|
+
matcher = Mongory::CMatcher.new({
|
142
142
|
'$or' => [
|
143
143
|
{ :age.gte => 18 },
|
144
144
|
{ status: 'active' }
|
145
145
|
]
|
146
|
-
)
|
146
|
+
})
|
147
147
|
5.times do
|
148
148
|
result = Benchmark.measure do
|
149
149
|
records.select { |r| matcher.match?(r) }
|
@@ -167,18 +167,4 @@ end
|
|
167
167
|
end
|
168
168
|
raise "count mismatch" if builder.count != count_of_complex_query
|
169
169
|
end
|
170
|
-
|
171
|
-
puts "\nTest Mongory::CMatcher#trace"
|
172
|
-
matcher = Mongory::CMatcher.new(
|
173
|
-
'$or' => [
|
174
|
-
{ :age.gte => 18 },
|
175
|
-
{ status: 'active' }
|
176
|
-
]
|
177
|
-
)
|
178
|
-
# matcher.enable_trace
|
179
|
-
records.sample(30).each do |r|
|
180
|
-
matcher.trace(r)
|
181
|
-
end
|
182
|
-
# matcher.print_trace
|
183
|
-
# matcher.disable_trace
|
184
170
|
end
|
data/ext/mongory_ext/extconf.rb
CHANGED
@@ -55,6 +55,13 @@ $CFLAGS << ' -Wno-declaration-after-statement -Wno-discarded-qualifiers'
|
|
55
55
|
$CFLAGS << ' -O2' unless ENV['DEBUG']
|
56
56
|
$CFLAGS << ' -g -O0 -DDEBUG' if ENV['DEBUG']
|
57
57
|
|
58
|
+
# --- macOS (darwin) linking: avoid hard-linking libruby, let symbols resolve at load time
|
59
|
+
if RbConfig::CONFIG['host_os'] =~ /darwin/
|
60
|
+
# Use dynamic_lookup so Ruby symbols (rb_*) are resolved by the host interpreter at dlopen time
|
61
|
+
$LDFLAGS << ' -Wl,-undefined,dynamic_lookup'
|
62
|
+
$DLDFLAGS << ' -Wl,-undefined,dynamic_lookup'
|
63
|
+
end
|
64
|
+
|
58
65
|
# Let mkmf generate rules by listing all sources and corresponding objects
|
59
66
|
$INCFLAGS << ' -I.'
|
60
67
|
$INCFLAGS << " -I#{File.join(core_src_dir, 'foundations')}"
|
@@ -74,6 +81,18 @@ puts "Use 'make' to build the extension"
|
|
74
81
|
# Append Makefile rules: explicit compilation rules for submodule sources to avoid copying/linking files
|
75
82
|
mk = File.read('Makefile')
|
76
83
|
|
84
|
+
if RbConfig::CONFIG['host_os'] =~ /darwin/
|
85
|
+
# --- sanitize Makefile to ensure no hard link against Ruby framework/libruby ---
|
86
|
+
mk << <<~'MAKE'
|
87
|
+
# --- darwin: force unlink from libruby (final override) ---
|
88
|
+
LIBRUBYARG =
|
89
|
+
LIBRUBYARG_SHARED =
|
90
|
+
LIBRUBYARG_STATIC =
|
91
|
+
LIBRUBY =
|
92
|
+
LIBS =
|
93
|
+
MAKE
|
94
|
+
end
|
95
|
+
|
77
96
|
# rubocop:disable Layout/HeredocIndentation
|
78
97
|
rules = +"\n# --- custom rules for mongory-core submodule sources ---\n"
|
79
98
|
(foundations_src + matchers_src).each do |src|
|
@@ -26,34 +26,34 @@ static VALUE inMongoryDataConverter;
|
|
26
26
|
static VALUE inMongoryConditionConverter;
|
27
27
|
|
28
28
|
// Matcher wrapper structure
|
29
|
-
typedef struct
|
29
|
+
typedef struct rb_mongory_matcher_t {
|
30
30
|
mongory_matcher *matcher;
|
31
31
|
mongory_value *condition;
|
32
32
|
mongory_memory_pool *pool;
|
33
33
|
mongory_memory_pool *scratch_pool;
|
34
|
+
mongory_memory_pool *trace_pool;
|
34
35
|
mongory_table *string_map;
|
35
36
|
mongory_table *symbol_map;
|
36
37
|
mongory_array *mark_list;
|
37
|
-
bool trace_enabled;
|
38
38
|
VALUE ctx;
|
39
|
-
}
|
39
|
+
} rb_mongory_matcher_t;
|
40
40
|
|
41
|
-
typedef struct
|
41
|
+
typedef struct rb_mongory_memory_pool_t {
|
42
42
|
mongory_memory_pool base;
|
43
|
-
|
44
|
-
}
|
43
|
+
rb_mongory_matcher_t *owner;
|
44
|
+
} rb_mongory_memory_pool_t;
|
45
45
|
|
46
|
-
typedef struct
|
46
|
+
typedef struct rb_mongory_table_t {
|
47
47
|
mongory_table base;
|
48
48
|
VALUE rb_hash;
|
49
|
-
|
50
|
-
}
|
49
|
+
rb_mongory_matcher_t *owner;
|
50
|
+
} rb_mongory_table_t;
|
51
51
|
|
52
|
-
typedef struct
|
52
|
+
typedef struct rb_mongory_array_t {
|
53
53
|
mongory_array base;
|
54
54
|
VALUE rb_array;
|
55
|
-
|
56
|
-
}
|
55
|
+
rb_mongory_matcher_t *owner;
|
56
|
+
} rb_mongory_array_t;
|
57
57
|
|
58
58
|
typedef struct {
|
59
59
|
mongory_table *table;
|
@@ -61,23 +61,23 @@ typedef struct {
|
|
61
61
|
} hash_conv_ctx;
|
62
62
|
|
63
63
|
// Forward declarations
|
64
|
-
static void
|
65
|
-
static void
|
66
|
-
mongory_value *
|
67
|
-
mongory_value *
|
68
|
-
mongory_value *
|
69
|
-
mongory_value *
|
70
|
-
static VALUE cache_fetch_string(
|
71
|
-
static VALUE cache_fetch_symbol(
|
72
|
-
static
|
73
|
-
static void rb_mongory_matcher_parse_argv(
|
74
|
-
static bool
|
75
|
-
|
76
|
-
static const rb_data_type_t
|
64
|
+
static void rb_mongory_matcher_mark(void *ptr);
|
65
|
+
static void rb_mongory_matcher_free(void *ptr);
|
66
|
+
mongory_value *rb_to_mongory_value_deep(mongory_memory_pool *pool, VALUE rb_value);
|
67
|
+
mongory_value *rb_to_mongory_value_shallow(mongory_memory_pool *pool, VALUE rb_value);
|
68
|
+
mongory_value *rb_mongory_table_wrap(mongory_memory_pool *pool, VALUE rb_hash);
|
69
|
+
mongory_value *rb_mongory_array_wrap(mongory_memory_pool *pool, VALUE rb_array);
|
70
|
+
static VALUE cache_fetch_string(rb_mongory_matcher_t *owner, const char *key);
|
71
|
+
static VALUE cache_fetch_symbol(rb_mongory_matcher_t *owner, const char *key);
|
72
|
+
static rb_mongory_memory_pool_t *rb_mongory_memory_pool_new();
|
73
|
+
static void rb_mongory_matcher_parse_argv(rb_mongory_matcher_t *wrapper, int argc, VALUE *argv);
|
74
|
+
static bool rb_mongory_error_handling(mongory_memory_pool *pool, char *error_message);
|
75
|
+
|
76
|
+
static const rb_data_type_t rb_mongory_matcher_type = {
|
77
77
|
.wrap_struct_name = "mongory_matcher",
|
78
78
|
.function = {
|
79
|
-
.dmark =
|
80
|
-
.dfree =
|
79
|
+
.dmark = rb_mongory_matcher_mark,
|
80
|
+
.dfree = rb_mongory_matcher_free,
|
81
81
|
.dsize = NULL,
|
82
82
|
},
|
83
83
|
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
@@ -88,30 +88,38 @@ static const rb_data_type_t ruby_mongory_matcher_type = {
|
|
88
88
|
*/
|
89
89
|
|
90
90
|
// Mongory::CMatcher.new(condition)
|
91
|
-
static VALUE
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
wrapper
|
97
|
-
wrapper->
|
98
|
-
wrapper->
|
99
|
-
wrapper->
|
100
|
-
wrapper->
|
91
|
+
static VALUE rb_mongory_matcher_new(int argc, VALUE *argv, VALUE class) {
|
92
|
+
rb_mongory_memory_pool_t *matcher_pool = rb_mongory_memory_pool_new();
|
93
|
+
mongory_memory_pool *matcher_pool_base = &matcher_pool->base;
|
94
|
+
rb_mongory_memory_pool_t *scratch_pool = rb_mongory_memory_pool_new();
|
95
|
+
mongory_memory_pool *scratch_pool_base = &scratch_pool->base;
|
96
|
+
rb_mongory_matcher_t *wrapper = ALLOC(rb_mongory_matcher_t);
|
97
|
+
wrapper->pool = matcher_pool_base;
|
98
|
+
wrapper->scratch_pool = scratch_pool_base;
|
99
|
+
wrapper->trace_pool = NULL;
|
100
|
+
wrapper->ctx = NULL;
|
101
|
+
wrapper->string_map = mongory_table_new(matcher_pool_base);
|
102
|
+
wrapper->symbol_map = mongory_table_new(matcher_pool_base);
|
103
|
+
wrapper->mark_list = mongory_array_new(matcher_pool_base);
|
104
|
+
wrapper->condition = NULL;
|
105
|
+
wrapper->matcher = NULL;
|
101
106
|
matcher_pool->owner = wrapper;
|
102
107
|
scratch_pool->owner = wrapper;
|
103
108
|
rb_mongory_matcher_parse_argv(wrapper, argc, argv);
|
104
109
|
|
105
|
-
mongory_matcher *matcher = mongory_matcher_new(
|
106
|
-
if (
|
110
|
+
mongory_matcher *matcher = mongory_matcher_new(matcher_pool_base, wrapper->condition, wrapper->ctx);
|
111
|
+
if (rb_mongory_error_handling(matcher_pool_base, "Failed to create matcher")) {
|
112
|
+
matcher_pool_base->free(matcher_pool_base);
|
113
|
+
scratch_pool_base->free(scratch_pool_base);
|
114
|
+
xfree(wrapper);
|
107
115
|
return Qnil;
|
108
116
|
}
|
109
117
|
|
110
118
|
wrapper->matcher = matcher;
|
111
|
-
return TypedData_Wrap_Struct(class, &
|
119
|
+
return TypedData_Wrap_Struct(class, &rb_mongory_matcher_type, wrapper);
|
112
120
|
}
|
113
121
|
|
114
|
-
static void rb_mongory_matcher_parse_argv(
|
122
|
+
static void rb_mongory_matcher_parse_argv(rb_mongory_matcher_t *self, int argc, VALUE *argv) {
|
115
123
|
VALUE condition, kw_hash;
|
116
124
|
rb_scan_args(argc, argv, "1:", &condition, &kw_hash);
|
117
125
|
const ID ctx_id[1] = { rb_intern("context") };
|
@@ -120,40 +128,50 @@ static void rb_mongory_matcher_parse_argv(ruby_mongory_matcher_t *wrapper, int a
|
|
120
128
|
rb_get_kwargs(kw_hash, ctx_id, 1, 0, kw_vals);
|
121
129
|
}
|
122
130
|
if (kw_vals[0] != Qundef) {
|
123
|
-
|
131
|
+
self->ctx = kw_vals[0];
|
124
132
|
} else {
|
125
|
-
|
133
|
+
self->ctx = rb_funcall(cMongoryMatcherContext, rb_intern("new"), 0);
|
126
134
|
}
|
127
135
|
VALUE converted_condition = rb_funcall(inMongoryConditionConverter, rb_intern("convert"), 1, condition);
|
128
|
-
|
129
|
-
|
130
|
-
mongory_value *store_ctx = mongory_value_wrap_u(
|
131
|
-
store_ctx->origin = (void *)
|
132
|
-
|
136
|
+
self->condition = rb_to_mongory_value_deep(self->pool, converted_condition);
|
137
|
+
self->mark_list->push(self->mark_list, self->condition);
|
138
|
+
mongory_value *store_ctx = mongory_value_wrap_u(self->pool, (void *)self->ctx);
|
139
|
+
store_ctx->origin = (void *)self->ctx;
|
140
|
+
self->mark_list->push(self->mark_list, store_ctx);
|
133
141
|
}
|
134
142
|
|
135
143
|
// Mongory::CMatcher#match(data)
|
136
|
-
static VALUE
|
137
|
-
|
138
|
-
TypedData_Get_Struct(self,
|
139
|
-
|
140
|
-
|
141
|
-
|
144
|
+
static VALUE rb_mongory_matcher_match(VALUE self, VALUE data) {
|
145
|
+
rb_mongory_matcher_t *self_wrapper;
|
146
|
+
TypedData_Get_Struct(self, rb_mongory_matcher_t, &rb_mongory_matcher_type, self_wrapper);
|
147
|
+
mongory_matcher *matcher = self_wrapper->matcher;
|
148
|
+
mongory_memory_pool *scratch_pool = self_wrapper->scratch_pool;
|
149
|
+
mongory_memory_pool *trace_pool = self_wrapper->trace_pool;
|
150
|
+
mongory_value *data_value = rb_to_mongory_value_shallow(scratch_pool, data);
|
151
|
+
|
152
|
+
if (rb_mongory_error_handling(scratch_pool, "Match failed")) {
|
142
153
|
return Qnil;
|
143
154
|
}
|
144
|
-
|
145
|
-
|
146
|
-
|
155
|
+
|
156
|
+
bool result = mongory_matcher_match(matcher, data_value);
|
157
|
+
|
158
|
+
if (trace_pool) {
|
159
|
+
mongory_matcher_print_trace(matcher);
|
160
|
+
trace_pool->reset(trace_pool);
|
161
|
+
mongory_matcher_enable_trace(matcher, trace_pool);
|
147
162
|
}
|
163
|
+
|
164
|
+
scratch_pool->reset(scratch_pool);
|
165
|
+
|
148
166
|
return result ? Qtrue : Qfalse;
|
149
167
|
}
|
150
168
|
|
151
169
|
// Mongory::CMatcher#explain
|
152
|
-
static VALUE
|
153
|
-
|
154
|
-
TypedData_Get_Struct(self,
|
170
|
+
static VALUE rb_mongory_matcher_explain(VALUE self) {
|
171
|
+
rb_mongory_matcher_t *wrapper;
|
172
|
+
TypedData_Get_Struct(self, rb_mongory_matcher_t, &rb_mongory_matcher_type, wrapper);
|
155
173
|
mongory_matcher_explain(wrapper->matcher, wrapper->scratch_pool);
|
156
|
-
if (
|
174
|
+
if (rb_mongory_error_handling(wrapper->scratch_pool, "Explain failed")) {
|
157
175
|
return Qnil;
|
158
176
|
}
|
159
177
|
wrapper->scratch_pool->reset(wrapper->scratch_pool);
|
@@ -161,78 +179,91 @@ static VALUE ruby_mongory_matcher_explain(VALUE self) {
|
|
161
179
|
}
|
162
180
|
|
163
181
|
// Mongory::CMatcher#trace(data)
|
164
|
-
static VALUE
|
165
|
-
|
166
|
-
TypedData_Get_Struct(self,
|
167
|
-
|
168
|
-
|
182
|
+
static VALUE rb_mongory_matcher_trace(VALUE self, VALUE data) {
|
183
|
+
rb_mongory_matcher_t *wrapper;
|
184
|
+
TypedData_Get_Struct(self, rb_mongory_matcher_t, &rb_mongory_matcher_type, wrapper);
|
185
|
+
mongory_memory_pool *trace_pool = mongory_memory_pool_new();
|
186
|
+
mongory_value *data_value = rb_to_mongory_value_shallow(trace_pool, data);
|
187
|
+
|
188
|
+
if (rb_mongory_error_handling(trace_pool, "Trace failed")) {
|
189
|
+
trace_pool->free(trace_pool);
|
169
190
|
return Qnil;
|
170
191
|
}
|
192
|
+
|
171
193
|
bool matched = mongory_matcher_trace(wrapper->matcher, data_value);
|
172
|
-
|
194
|
+
trace_pool->free(trace_pool);
|
195
|
+
|
173
196
|
return matched ? Qtrue : Qfalse;
|
174
197
|
}
|
175
198
|
|
176
199
|
// Mongory::CMatcher#enable_trace
|
177
|
-
static VALUE
|
178
|
-
|
179
|
-
TypedData_Get_Struct(self,
|
180
|
-
|
181
|
-
|
200
|
+
static VALUE rb_mongory_matcher_enable_trace(VALUE self) {
|
201
|
+
rb_mongory_matcher_t *wrapper;
|
202
|
+
TypedData_Get_Struct(self, rb_mongory_matcher_t, &rb_mongory_matcher_type, wrapper);
|
203
|
+
mongory_memory_pool *trace_pool = rb_mongory_memory_pool_new();
|
204
|
+
mongory_matcher_enable_trace(wrapper->matcher, trace_pool);
|
205
|
+
|
206
|
+
if (rb_mongory_error_handling(trace_pool, "Enable trace failed")) {
|
207
|
+
trace_pool->free(trace_pool);
|
182
208
|
return Qnil;
|
183
209
|
}
|
184
|
-
wrapper->
|
210
|
+
wrapper->trace_pool = trace_pool;
|
211
|
+
|
185
212
|
return Qnil;
|
186
213
|
}
|
187
214
|
|
188
215
|
// Mongory::CMatcher#disable_trace
|
189
|
-
static VALUE
|
190
|
-
|
191
|
-
TypedData_Get_Struct(self,
|
216
|
+
static VALUE rb_mongory_matcher_disable_trace(VALUE self) {
|
217
|
+
rb_mongory_matcher_t *wrapper;
|
218
|
+
TypedData_Get_Struct(self, rb_mongory_matcher_t, &rb_mongory_matcher_type, wrapper);
|
219
|
+
mongory_memory_pool *trace_pool = wrapper->trace_pool;
|
192
220
|
mongory_matcher_disable_trace(wrapper->matcher);
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
wrapper->trace_enabled = false;
|
221
|
+
rb_mongory_error_handling(trace_pool, "Disable trace failed");
|
222
|
+
trace_pool->free(trace_pool);
|
223
|
+
wrapper->trace_pool = NULL;
|
224
|
+
|
198
225
|
return Qnil;
|
199
226
|
}
|
200
227
|
|
201
228
|
// Mongory::CMatcher#print_trace
|
202
|
-
static VALUE
|
203
|
-
|
204
|
-
TypedData_Get_Struct(self,
|
229
|
+
static VALUE rb_mongory_matcher_print_trace(VALUE self) {
|
230
|
+
rb_mongory_matcher_t *wrapper;
|
231
|
+
TypedData_Get_Struct(self, rb_mongory_matcher_t, &rb_mongory_matcher_type, wrapper);
|
205
232
|
mongory_matcher_print_trace(wrapper->matcher);
|
206
|
-
|
233
|
+
rb_mongory_error_handling(wrapper->trace_pool, "Print trace failed");
|
234
|
+
|
207
235
|
return Qnil;
|
208
236
|
}
|
209
237
|
|
210
238
|
// Mongory::CMatcher#condition
|
211
|
-
static VALUE
|
212
|
-
|
213
|
-
TypedData_Get_Struct(self,
|
239
|
+
static VALUE rb_mongory_matcher_condition(VALUE self) {
|
240
|
+
rb_mongory_matcher_t *wrapper;
|
241
|
+
TypedData_Get_Struct(self, rb_mongory_matcher_t, &rb_mongory_matcher_type, wrapper);
|
242
|
+
|
214
243
|
return (VALUE)wrapper->condition->origin;
|
215
244
|
}
|
216
245
|
|
217
246
|
// Mongory::CMatcher#context
|
218
|
-
static VALUE
|
219
|
-
|
220
|
-
TypedData_Get_Struct(self,
|
247
|
+
static VALUE rb_mongory_matcher_context(VALUE self) {
|
248
|
+
rb_mongory_matcher_t *wrapper;
|
249
|
+
TypedData_Get_Struct(self, rb_mongory_matcher_t, &rb_mongory_matcher_type, wrapper);
|
250
|
+
|
221
251
|
return wrapper->ctx ? wrapper->ctx : Qnil;
|
222
252
|
}
|
223
253
|
|
224
254
|
// Mongory::CMatcher.trace_result_colorful=(colorful)
|
225
|
-
static VALUE
|
255
|
+
static VALUE rb_mongory_matcher_trace_result_colorful(VALUE self, VALUE colorful) {
|
226
256
|
(void)self;
|
227
257
|
mongory_matcher_trace_result_colorful_set(RTEST(colorful));
|
258
|
+
|
228
259
|
return Qnil;
|
229
260
|
}
|
230
261
|
|
231
262
|
/**
|
232
263
|
* Create a new memory pool
|
233
264
|
*/
|
234
|
-
static
|
235
|
-
|
265
|
+
static rb_mongory_memory_pool_t *rb_mongory_memory_pool_new() {
|
266
|
+
rb_mongory_memory_pool_t *pool = malloc(sizeof(rb_mongory_memory_pool_t));
|
236
267
|
mongory_memory_pool *base = mongory_memory_pool_new();
|
237
268
|
memcpy(&pool->base, base, sizeof(mongory_memory_pool));
|
238
269
|
free(base);
|
@@ -243,12 +274,16 @@ static ruby_mongory_memory_pool_t *ruby_mongory_memory_pool_new() {
|
|
243
274
|
/**
|
244
275
|
* Ruby GC management functions
|
245
276
|
*/
|
246
|
-
static void
|
247
|
-
|
277
|
+
static void rb_mongory_matcher_free(void *ptr) {
|
278
|
+
rb_mongory_matcher_t *wrapper = (rb_mongory_matcher_t *)ptr;
|
248
279
|
mongory_memory_pool *pool = wrapper->pool;
|
249
280
|
mongory_memory_pool *scratch_pool = wrapper->scratch_pool;
|
281
|
+
mongory_memory_pool *trace_pool = wrapper->trace_pool;
|
250
282
|
pool->free(pool);
|
251
283
|
scratch_pool->free(scratch_pool);
|
284
|
+
if (trace_pool) {
|
285
|
+
trace_pool->free(trace_pool);
|
286
|
+
}
|
252
287
|
xfree(wrapper);
|
253
288
|
}
|
254
289
|
|
@@ -264,10 +299,10 @@ static bool gc_mark_array_cb(mongory_value *value, void *acc) {
|
|
264
299
|
/**
|
265
300
|
* GC marking callback for mongory_matcher
|
266
301
|
*/
|
267
|
-
static void
|
268
|
-
|
269
|
-
if (!
|
270
|
-
|
302
|
+
static void rb_mongory_matcher_mark(void *ptr) {
|
303
|
+
rb_mongory_matcher_t *self = (rb_mongory_matcher_t *)ptr;
|
304
|
+
if (!self) return;
|
305
|
+
self->mark_list->each(self->mark_list, NULL, gc_mark_array_cb);
|
271
306
|
}
|
272
307
|
|
273
308
|
/**
|
@@ -275,7 +310,7 @@ static void ruby_mongory_matcher_mark(void *ptr) {
|
|
275
310
|
*/
|
276
311
|
|
277
312
|
// Helper function to convert Ruby value to C string
|
278
|
-
static char *
|
313
|
+
static char *rb_mongory_value_to_cstr(mongory_value *value, mongory_memory_pool *pool) {
|
279
314
|
(void)pool;
|
280
315
|
VALUE rb_value = (VALUE)value->origin;
|
281
316
|
VALUE rb_str = rb_funcall(rb_value, rb_intern("inspect"), 0);
|
@@ -283,7 +318,7 @@ static char *ruby_mongory_value_to_cstr(mongory_value *value, mongory_memory_poo
|
|
283
318
|
}
|
284
319
|
|
285
320
|
// Helper function for primitive conversion of Ruby value to mongory_value
|
286
|
-
static mongory_value *
|
321
|
+
static mongory_value *rb_to_mongory_value_primitive(mongory_memory_pool *pool, VALUE rb_value) {
|
287
322
|
mongory_value *mg_value = NULL;
|
288
323
|
switch (TYPE(rb_value)) {
|
289
324
|
case T_NIL:
|
@@ -325,8 +360,8 @@ static mongory_value *ruby_to_mongory_value_primitive(mongory_memory_pool *pool,
|
|
325
360
|
return mg_value;
|
326
361
|
}
|
327
362
|
// Shallow conversion: Convert Ruby value to mongory_value (fully materialize arrays/tables)
|
328
|
-
static mongory_value *
|
329
|
-
mongory_value *mg_value =
|
363
|
+
static mongory_value *rb_to_mongory_value_shallow_rec(mongory_memory_pool *pool, VALUE rb_value, bool converted) {
|
364
|
+
mongory_value *mg_value = rb_to_mongory_value_primitive(pool, rb_value);
|
330
365
|
if (mg_value) {
|
331
366
|
mg_value->origin = rb_value;
|
332
367
|
return mg_value;
|
@@ -334,12 +369,12 @@ static mongory_value *ruby_to_mongory_value_shallow_rec(mongory_memory_pool *poo
|
|
334
369
|
|
335
370
|
switch (TYPE(rb_value)) {
|
336
371
|
case T_ARRAY: {
|
337
|
-
mg_value =
|
372
|
+
mg_value = rb_mongory_array_wrap(pool, rb_value);
|
338
373
|
break;
|
339
374
|
}
|
340
375
|
|
341
376
|
case T_HASH: {
|
342
|
-
mg_value =
|
377
|
+
mg_value = rb_mongory_table_wrap(pool, rb_value);
|
343
378
|
break;
|
344
379
|
}
|
345
380
|
|
@@ -349,7 +384,7 @@ static mongory_value *ruby_to_mongory_value_shallow_rec(mongory_memory_pool *poo
|
|
349
384
|
break;
|
350
385
|
} else {
|
351
386
|
VALUE converted_value = rb_funcall(inMongoryDataConverter, rb_intern("convert"), 1, rb_value);
|
352
|
-
return
|
387
|
+
return rb_to_mongory_value_shallow_rec(pool, converted_value, true);
|
353
388
|
}
|
354
389
|
}
|
355
390
|
mg_value->origin = rb_value;
|
@@ -357,15 +392,15 @@ static mongory_value *ruby_to_mongory_value_shallow_rec(mongory_memory_pool *poo
|
|
357
392
|
}
|
358
393
|
|
359
394
|
// Shallow conversion: Convert Ruby value to mongory_value (fully materialize arrays/tables)
|
360
|
-
mongory_value *
|
361
|
-
return
|
395
|
+
mongory_value *rb_to_mongory_value_shallow(mongory_memory_pool *pool, VALUE rb_value) {
|
396
|
+
return rb_to_mongory_value_shallow_rec(pool, rb_value, false);
|
362
397
|
}
|
363
398
|
|
364
399
|
// Helper function for deep conversion of hash values
|
365
400
|
static int hash_foreach_deep_convert_cb(VALUE key, VALUE val, VALUE ptr) {
|
366
401
|
hash_conv_ctx *ctx = (hash_conv_ctx *)ptr;
|
367
|
-
|
368
|
-
|
402
|
+
rb_mongory_memory_pool_t *rb_pool = (rb_mongory_memory_pool_t *)ctx->pool;
|
403
|
+
rb_mongory_matcher_t *owner = rb_pool->owner;
|
369
404
|
mongory_table *store_map;
|
370
405
|
char *key_str;
|
371
406
|
if (SYMBOL_P(key)) {
|
@@ -378,14 +413,14 @@ static int hash_foreach_deep_convert_cb(VALUE key, VALUE val, VALUE ptr) {
|
|
378
413
|
mongory_value *store = mongory_value_wrap_u(ctx->pool, NULL);
|
379
414
|
store->origin = (void *)key;
|
380
415
|
store_map->set(store_map, key_str, store);
|
381
|
-
mongory_value *cval =
|
416
|
+
mongory_value *cval = rb_to_mongory_value_deep(ctx->pool, val);
|
382
417
|
ctx->table->set(ctx->table, key_str, cval);
|
383
418
|
return ST_CONTINUE;
|
384
419
|
}
|
385
420
|
|
386
421
|
// Deep conversion: Convert Ruby value to mongory_value (fully materialize arrays/tables)
|
387
|
-
static mongory_value *
|
388
|
-
mongory_value *mg_value =
|
422
|
+
static mongory_value *rb_to_mongory_value_deep_rec(mongory_memory_pool *pool, VALUE rb_value, bool converted) {
|
423
|
+
mongory_value *mg_value = rb_to_mongory_value_primitive(pool, rb_value);
|
389
424
|
if (mg_value) {
|
390
425
|
mg_value->origin = rb_value;
|
391
426
|
return mg_value;
|
@@ -395,7 +430,7 @@ static mongory_value *ruby_to_mongory_value_deep_rec(mongory_memory_pool *pool,
|
|
395
430
|
mongory_array *array = mongory_array_new(pool);
|
396
431
|
|
397
432
|
for (long i = 0; i < RARRAY_LEN(rb_value); i++) {
|
398
|
-
array->push(array,
|
433
|
+
array->push(array, rb_to_mongory_value_deep(pool, RARRAY_AREF(rb_value, i)));
|
399
434
|
}
|
400
435
|
mg_value = mongory_value_wrap_a(pool, array);
|
401
436
|
break;
|
@@ -417,7 +452,7 @@ static mongory_value *ruby_to_mongory_value_deep_rec(mongory_memory_pool *pool,
|
|
417
452
|
break;
|
418
453
|
} else {
|
419
454
|
VALUE converted_value = rb_funcall(inMongoryDataConverter, rb_intern("convert"), 1, rb_value);
|
420
|
-
mg_value =
|
455
|
+
mg_value = rb_to_mongory_value_deep_rec(pool, converted_value, true);
|
421
456
|
break;
|
422
457
|
}
|
423
458
|
}
|
@@ -426,13 +461,13 @@ static mongory_value *ruby_to_mongory_value_deep_rec(mongory_memory_pool *pool,
|
|
426
461
|
}
|
427
462
|
|
428
463
|
// Deep conversion: Convert Ruby value to mongory_value (fully materialize arrays/tables)
|
429
|
-
mongory_value *
|
430
|
-
return
|
464
|
+
mongory_value *rb_to_mongory_value_deep(mongory_memory_pool *pool, VALUE rb_value) {
|
465
|
+
return rb_to_mongory_value_deep_rec(pool, rb_value, false);
|
431
466
|
}
|
432
467
|
|
433
468
|
// Get value from Ruby hash via mongory_table
|
434
|
-
mongory_value *
|
435
|
-
|
469
|
+
mongory_value *rb_mongory_table_get(mongory_table *self, char *key) {
|
470
|
+
rb_mongory_table_t *table = (rb_mongory_table_t *)self;
|
436
471
|
VALUE rb_hash = table->rb_hash;
|
437
472
|
VALUE rb_value = Qundef;
|
438
473
|
|
@@ -449,47 +484,47 @@ mongory_value *ruby_mongory_table_get(mongory_table *self, char *key) {
|
|
449
484
|
if (rb_value == Qundef) {
|
450
485
|
return NULL;
|
451
486
|
}
|
452
|
-
return
|
487
|
+
return rb_to_mongory_value_shallow(table->base.pool, rb_value);
|
453
488
|
}
|
454
489
|
|
455
490
|
// Shallow conversion: Wrap Ruby hash as mongory_table
|
456
|
-
mongory_value *
|
457
|
-
|
458
|
-
|
491
|
+
mongory_value *rb_mongory_table_wrap(mongory_memory_pool *pool, VALUE rb_hash) {
|
492
|
+
rb_mongory_table_t *table = MG_ALLOC_PTR(pool, rb_mongory_table_t);
|
493
|
+
rb_mongory_memory_pool_t *rb_pool = (rb_mongory_memory_pool_t *)pool;
|
459
494
|
table->base.pool = pool;
|
460
|
-
table->base.get =
|
495
|
+
table->base.get = rb_mongory_table_get;
|
461
496
|
table->rb_hash = rb_hash;
|
462
497
|
table->base.count = RHASH_SIZE(rb_hash);
|
463
498
|
table->owner = rb_pool->owner;
|
464
499
|
mongory_value *mg_value = mongory_value_wrap_t(pool, &table->base);
|
465
500
|
mg_value->origin = (void *)rb_hash;
|
466
|
-
mg_value->to_str =
|
501
|
+
mg_value->to_str = rb_mongory_value_to_cstr;
|
467
502
|
return mg_value;
|
468
503
|
}
|
469
504
|
|
470
505
|
// Get value from Ruby array via mongory_array
|
471
|
-
static mongory_value *
|
472
|
-
|
506
|
+
static mongory_value *rb_mongory_array_get(mongory_array *self, size_t index) {
|
507
|
+
rb_mongory_array_t *array = (rb_mongory_array_t *)self;
|
473
508
|
VALUE rb_array = array->rb_array;
|
474
509
|
if (index >= (size_t)RARRAY_LEN(rb_array)) {
|
475
510
|
return NULL;
|
476
511
|
}
|
477
512
|
VALUE rb_value = rb_ary_entry(rb_array, index);
|
478
|
-
return
|
513
|
+
return rb_to_mongory_value_shallow(self->pool, rb_value);
|
479
514
|
}
|
480
515
|
|
481
516
|
// Shallow conversion: Wrap Ruby array as mongory_array
|
482
|
-
mongory_value *
|
483
|
-
|
484
|
-
|
517
|
+
mongory_value *rb_mongory_array_wrap(mongory_memory_pool *pool, VALUE rb_array) {
|
518
|
+
rb_mongory_array_t *array = MG_ALLOC_PTR(pool, rb_mongory_array_t);
|
519
|
+
rb_mongory_memory_pool_t *rb_pool = (rb_mongory_memory_pool_t *)pool;
|
485
520
|
array->base.pool = pool;
|
486
|
-
array->base.get =
|
521
|
+
array->base.get = rb_mongory_array_get;
|
487
522
|
array->rb_array = rb_array;
|
488
523
|
array->base.count = RARRAY_LEN(rb_array);
|
489
524
|
array->owner = rb_pool->owner;
|
490
525
|
mongory_value *mg_value = mongory_value_wrap_a(pool, &array->base);
|
491
526
|
mg_value->origin = (void *)rb_array;
|
492
|
-
mg_value->to_str =
|
527
|
+
mg_value->to_str = rb_mongory_value_to_cstr;
|
493
528
|
return mg_value;
|
494
529
|
}
|
495
530
|
|
@@ -502,7 +537,7 @@ void *mongory_value_to_ruby(mongory_memory_pool *pool, mongory_value *value) {
|
|
502
537
|
}
|
503
538
|
|
504
539
|
// ===== Cache helper implementations =====
|
505
|
-
static VALUE cache_fetch_string(
|
540
|
+
static VALUE cache_fetch_string(rb_mongory_matcher_t *owner, const char *key) {
|
506
541
|
if (!owner || !owner->string_map) return rb_utf8_str_new_cstr(key);
|
507
542
|
mongory_value *v = owner->string_map->get(owner->string_map, (char *)key);
|
508
543
|
if (v && v->origin) return (VALUE)v->origin;
|
@@ -522,7 +557,7 @@ static inline VALUE char_key_to_symbol(const char *key, rb_encoding *enc) {
|
|
522
557
|
}
|
523
558
|
|
524
559
|
// Cache helper for Ruby symbol keys
|
525
|
-
static VALUE cache_fetch_symbol(
|
560
|
+
static VALUE cache_fetch_symbol(rb_mongory_matcher_t *owner, const char *key) {
|
526
561
|
rb_encoding *enc = rb_utf8_encoding();
|
527
562
|
if (!owner || !owner->symbol_map) {
|
528
563
|
return char_key_to_symbol(key, enc);
|
@@ -538,7 +573,7 @@ static VALUE cache_fetch_symbol(ruby_mongory_matcher_t *owner, const char *key)
|
|
538
573
|
}
|
539
574
|
|
540
575
|
// Regex adapter bridging to Ruby's Regexp
|
541
|
-
static bool
|
576
|
+
static bool rb_mongory_regex_match_adapter(mongory_memory_pool *pool, mongory_value *pattern, mongory_value *value) {
|
542
577
|
if (!pattern || !value) {
|
543
578
|
return false;
|
544
579
|
}
|
@@ -565,7 +600,7 @@ static bool ruby_regex_match_adapter(mongory_memory_pool *pool, mongory_value *p
|
|
565
600
|
}
|
566
601
|
|
567
602
|
// Regex adapter bridging to Ruby's Regexp
|
568
|
-
static char *
|
603
|
+
static char *rb_mongory_regex_stringify_adapter(mongory_memory_pool *pool, mongory_value *pattern) {
|
569
604
|
(void)pool;
|
570
605
|
if (pattern->type != MONGORY_TYPE_REGEX) {
|
571
606
|
return NULL;
|
@@ -576,10 +611,10 @@ static char *ruby_regex_stringify_adapter(mongory_memory_pool *pool, mongory_val
|
|
576
611
|
}
|
577
612
|
|
578
613
|
// Custom matcher adapter bridging to Ruby's custom matcher
|
579
|
-
static mongory_matcher_custom_context *
|
614
|
+
static mongory_matcher_custom_context *rb_mongory_custom_matcher_build(char *key, mongory_value *condition, void *ctx) {
|
580
615
|
mongory_memory_pool *pool = condition->pool;
|
581
|
-
|
582
|
-
|
616
|
+
rb_mongory_memory_pool_t *rb_pool = (rb_mongory_memory_pool_t *)pool;
|
617
|
+
rb_mongory_matcher_t *owner = rb_pool->owner;
|
583
618
|
VALUE matcher_class = rb_funcall(mMongoryMatchers, rb_intern("lookup"), 1, cache_fetch_string(owner, key));
|
584
619
|
if (matcher_class == Qnil) {
|
585
620
|
return NULL;
|
@@ -611,24 +646,23 @@ static mongory_matcher_custom_context *ruby_custom_matcher_build(char *key, mong
|
|
611
646
|
}
|
612
647
|
|
613
648
|
// Custom matcher adapter bridging to Ruby's custom matcher
|
614
|
-
static bool
|
649
|
+
static bool rb_mongory_custom_matcher_match(void *ruby_matcher, mongory_value *value) {
|
615
650
|
VALUE matcher = (VALUE)ruby_matcher;
|
616
651
|
VALUE match_result = rb_funcall(matcher, rb_intern("match?"), 1, value->origin);
|
617
652
|
return RTEST(match_result);
|
618
653
|
}
|
619
654
|
|
620
655
|
// Custom matcher adapter bridging to Ruby's custom matcher
|
621
|
-
static bool
|
656
|
+
static bool rb_mongory_custom_matcher_lookup(char *key) {
|
622
657
|
VALUE matcher_class = rb_funcall(mMongoryMatchers, rb_intern("lookup"), 1, rb_str_new_cstr(key));
|
623
658
|
return RTEST(matcher_class);
|
624
659
|
}
|
625
660
|
|
626
661
|
// Error handling for mongory_memory_pool
|
627
|
-
static bool
|
662
|
+
static bool rb_mongory_error_handling(mongory_memory_pool *pool, char *error_message) {
|
628
663
|
if (pool->error) {
|
629
664
|
rb_raise(eMongoryTypeError, "%s: %s", error_message, pool->error->message);
|
630
665
|
pool->error = NULL;
|
631
|
-
pool->reset(pool);
|
632
666
|
return true;
|
633
667
|
}
|
634
668
|
return false;
|
@@ -656,28 +690,28 @@ void Init_mongory_ext(void) {
|
|
656
690
|
eMongoryTypeError = rb_define_class_under(mMongory, "TypeError", eMongoryError);
|
657
691
|
|
658
692
|
// Define Matcher methods
|
659
|
-
rb_define_singleton_method(cMongoryMatcher, "new",
|
660
|
-
rb_define_singleton_method(cMongoryMatcher, "trace_result_colorful=",
|
661
|
-
rb_define_method(cMongoryMatcher, "match?",
|
662
|
-
rb_define_method(cMongoryMatcher, "explain",
|
663
|
-
rb_define_method(cMongoryMatcher, "condition",
|
664
|
-
rb_define_method(cMongoryMatcher, "context",
|
665
|
-
rb_define_method(cMongoryMatcher, "trace",
|
666
|
-
rb_define_method(cMongoryMatcher, "enable_trace",
|
667
|
-
rb_define_method(cMongoryMatcher, "disable_trace",
|
668
|
-
rb_define_method(cMongoryMatcher, "print_trace",
|
693
|
+
rb_define_singleton_method(cMongoryMatcher, "new", rb_mongory_matcher_new, -1);
|
694
|
+
rb_define_singleton_method(cMongoryMatcher, "trace_result_colorful=", rb_mongory_matcher_trace_result_colorful, 1);
|
695
|
+
rb_define_method(cMongoryMatcher, "match?", rb_mongory_matcher_match, 1);
|
696
|
+
rb_define_method(cMongoryMatcher, "explain", rb_mongory_matcher_explain, 0);
|
697
|
+
rb_define_method(cMongoryMatcher, "condition", rb_mongory_matcher_condition, 0);
|
698
|
+
rb_define_method(cMongoryMatcher, "context", rb_mongory_matcher_context, 0);
|
699
|
+
rb_define_method(cMongoryMatcher, "trace", rb_mongory_matcher_trace, 1);
|
700
|
+
rb_define_method(cMongoryMatcher, "enable_trace", rb_mongory_matcher_enable_trace, 0);
|
701
|
+
rb_define_method(cMongoryMatcher, "disable_trace", rb_mongory_matcher_disable_trace, 0);
|
702
|
+
rb_define_method(cMongoryMatcher, "print_trace", rb_mongory_matcher_print_trace, 0);
|
669
703
|
|
670
704
|
// Set regex adapter to use Ruby's Regexp
|
671
|
-
mongory_regex_func_set(
|
672
|
-
mongory_regex_stringify_func_set(
|
705
|
+
mongory_regex_func_set(rb_mongory_regex_match_adapter);
|
706
|
+
mongory_regex_stringify_func_set(rb_mongory_regex_stringify_adapter);
|
673
707
|
|
674
708
|
// Set value converter functions
|
675
|
-
mongory_value_converter_deep_convert_set(
|
676
|
-
mongory_value_converter_shallow_convert_set(
|
709
|
+
mongory_value_converter_deep_convert_set(rb_to_mongory_value_deep);
|
710
|
+
mongory_value_converter_shallow_convert_set(rb_to_mongory_value_shallow);
|
677
711
|
mongory_value_converter_recover_set(mongory_value_to_ruby);
|
678
712
|
|
679
713
|
// Set custom matcher adapter
|
680
|
-
mongory_custom_matcher_match_func_set(
|
681
|
-
mongory_custom_matcher_build_func_set(
|
682
|
-
mongory_custom_matcher_lookup_func_set(
|
714
|
+
mongory_custom_matcher_match_func_set(rb_mongory_custom_matcher_match);
|
715
|
+
mongory_custom_matcher_build_func_set(rb_mongory_custom_matcher_build);
|
716
|
+
mongory_custom_matcher_lookup_func_set(rb_mongory_custom_matcher_lookup);
|
683
717
|
}
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mongory
|
4
|
+
# This class is the C extension of Mongory::QueryMatcher.
|
5
|
+
# It is used to match records using the C extension.
|
6
|
+
# @example
|
7
|
+
# matcher = Mongory::CMatcher.new(condition)
|
8
|
+
# matcher.match?(record) #=> true
|
9
|
+
# # or
|
10
|
+
# collection.mongory.c.where(condition).to_a
|
11
|
+
class CMatcher
|
12
|
+
# @!method self.new(condition)
|
13
|
+
# @param condition [Object] the condition
|
14
|
+
# @return [Mongory::CMatcher] a new matcher
|
15
|
+
# @note This method is implemented in the C extension
|
16
|
+
#
|
17
|
+
# @!method match?(record)
|
18
|
+
# @param record [Object] the record to match against
|
19
|
+
# @return [Boolean] true if the record matches the condition, false otherwise
|
20
|
+
# @note This method is implemented in the C extension
|
21
|
+
# @!method explain
|
22
|
+
# @return [void]
|
23
|
+
# @note This method will print metcher tree structure
|
24
|
+
# @note This method is implemented in the C extension
|
25
|
+
# @!method trace
|
26
|
+
# @return [Boolean] true if the record matches the condition, false otherwise
|
27
|
+
# @note This method will print matching process
|
28
|
+
# @note This method is implemented in the C extension
|
29
|
+
# @!method enable_trace
|
30
|
+
# @return [void]
|
31
|
+
# @note This method will enable trace
|
32
|
+
# @note This method is implemented in the C extension
|
33
|
+
# @!method disable_trace
|
34
|
+
# @return [void]
|
35
|
+
# @note This method will disable trace
|
36
|
+
# @note This method is implemented in the C extension
|
37
|
+
# @!method print_trace
|
38
|
+
# @return [void]
|
39
|
+
# @note This method will print trace result
|
40
|
+
# @note This method is implemented in the C extension
|
41
|
+
# @!method condition
|
42
|
+
# @return [Object] the condition
|
43
|
+
# @note This method is implemented in the C extension
|
44
|
+
# @!method context
|
45
|
+
# @return [Utils::Context] the context
|
46
|
+
# @note This method is implemented in the C extension
|
47
|
+
# @!method trace_result_colorful=
|
48
|
+
# @param colorful [Boolean] whether to enable colorful trace result
|
49
|
+
# @return [Boolean]
|
50
|
+
# @note This method is implemented in the C extension
|
51
|
+
|
52
|
+
# @return [Proc] a Proc that performs the matching operation
|
53
|
+
def to_proc
|
54
|
+
Proc.new { |record| match?(record) }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -65,10 +65,27 @@ module Mongory
|
|
65
65
|
@matcher.prepare_query
|
66
66
|
matcher_block = @matcher.to_proc
|
67
67
|
@records.each do |record|
|
68
|
+
@context.current_record = record
|
68
69
|
yield record if matcher_block.call(record)
|
69
70
|
end
|
70
71
|
end
|
71
72
|
|
73
|
+
def trace
|
74
|
+
return to_enum(:trace) unless block_given?
|
75
|
+
|
76
|
+
Mongory.debugger.enable
|
77
|
+
@matcher.prepare_query
|
78
|
+
@records.each do |record|
|
79
|
+
@context.current_record = record
|
80
|
+
matched = @matcher.match?(record)
|
81
|
+
Mongory.debugger.display
|
82
|
+
yield record if matched
|
83
|
+
Mongory.debugger.clear
|
84
|
+
end
|
85
|
+
ensure
|
86
|
+
Mongory.debugger.disable
|
87
|
+
end
|
88
|
+
|
72
89
|
# Adds a condition to filter records using the given condition.
|
73
90
|
# This is an alias for `and`.
|
74
91
|
#
|
data/lib/mongory/version.rb
CHANGED
data/lib/mongory.rb
CHANGED
@@ -120,6 +120,7 @@ begin
|
|
120
120
|
abi = RUBY_VERSION.split('.').first(2).join('.')
|
121
121
|
require "core/#{abi}/mongory_ext"
|
122
122
|
require_relative 'mongory/c_query_builder'
|
123
|
-
|
124
|
-
|
123
|
+
require_relative 'mongory/c_matcher'
|
124
|
+
rescue LoadError => e
|
125
|
+
warn("Mongory C extension is disabled because mongory_ext is not loaded: #{e.message}")
|
125
126
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongory
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: x86_64-darwin
|
6
6
|
authors:
|
7
7
|
- koten0224
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-08-
|
11
|
+
date: 2025-08-30 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A Mongo-like in-memory query DSL for Ruby
|
14
14
|
email:
|
@@ -90,6 +90,7 @@ files:
|
|
90
90
|
- lib/generators/mongory/matcher/templates/matcher.rb.erb
|
91
91
|
- lib/generators/mongory/matcher/templates/matcher_spec.rb.erb
|
92
92
|
- lib/mongory.rb
|
93
|
+
- lib/mongory/c_matcher.rb
|
93
94
|
- lib/mongory/c_query_builder.rb
|
94
95
|
- lib/mongory/converters.rb
|
95
96
|
- lib/mongory/converters/abstract_converter.rb
|