mongory 0.7.2-arm64-darwin-23

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 (115) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +88 -0
  4. data/.yardopts +7 -0
  5. data/CHANGELOG.md +364 -0
  6. data/CODE_OF_CONDUCT.md +84 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +488 -0
  9. data/Rakefile +107 -0
  10. data/SUBMODULE_INTEGRATION.md +325 -0
  11. data/docs/advanced_usage.md +40 -0
  12. data/docs/clang_bridge.md +69 -0
  13. data/docs/field_names.md +30 -0
  14. data/docs/migration.md +30 -0
  15. data/docs/performance.md +61 -0
  16. data/examples/README.md +41 -0
  17. data/examples/benchmark-rails.rb +52 -0
  18. data/examples/benchmark.rb +184 -0
  19. data/ext/mongory_ext/extconf.rb +91 -0
  20. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/array.h +122 -0
  21. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/config.h +161 -0
  22. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/error.h +79 -0
  23. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/memory_pool.h +95 -0
  24. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/table.h +127 -0
  25. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/value.h +175 -0
  26. data/ext/mongory_ext/mongory-core/include/mongory-core/matchers/matcher.h +76 -0
  27. data/ext/mongory_ext/mongory-core/include/mongory-core.h +12 -0
  28. data/ext/mongory_ext/mongory-core/src/foundations/array.c +287 -0
  29. data/ext/mongory_ext/mongory-core/src/foundations/array_private.h +19 -0
  30. data/ext/mongory_ext/mongory-core/src/foundations/config.c +270 -0
  31. data/ext/mongory_ext/mongory-core/src/foundations/config_private.h +48 -0
  32. data/ext/mongory_ext/mongory-core/src/foundations/error.c +38 -0
  33. data/ext/mongory_ext/mongory-core/src/foundations/memory_pool.c +298 -0
  34. data/ext/mongory_ext/mongory-core/src/foundations/string_buffer.c +65 -0
  35. data/ext/mongory_ext/mongory-core/src/foundations/string_buffer.h +49 -0
  36. data/ext/mongory_ext/mongory-core/src/foundations/table.c +498 -0
  37. data/ext/mongory_ext/mongory-core/src/foundations/utils.c +210 -0
  38. data/ext/mongory_ext/mongory-core/src/foundations/utils.h +70 -0
  39. data/ext/mongory_ext/mongory-core/src/foundations/value.c +500 -0
  40. data/ext/mongory_ext/mongory-core/src/matchers/array_record_matcher.c +164 -0
  41. data/ext/mongory_ext/mongory-core/src/matchers/array_record_matcher.h +47 -0
  42. data/ext/mongory_ext/mongory-core/src/matchers/base_matcher.c +122 -0
  43. data/ext/mongory_ext/mongory-core/src/matchers/base_matcher.h +100 -0
  44. data/ext/mongory_ext/mongory-core/src/matchers/compare_matcher.c +217 -0
  45. data/ext/mongory_ext/mongory-core/src/matchers/compare_matcher.h +83 -0
  46. data/ext/mongory_ext/mongory-core/src/matchers/composite_matcher.c +573 -0
  47. data/ext/mongory_ext/mongory-core/src/matchers/composite_matcher.h +125 -0
  48. data/ext/mongory_ext/mongory-core/src/matchers/existance_matcher.c +147 -0
  49. data/ext/mongory_ext/mongory-core/src/matchers/existance_matcher.h +48 -0
  50. data/ext/mongory_ext/mongory-core/src/matchers/external_matcher.c +124 -0
  51. data/ext/mongory_ext/mongory-core/src/matchers/external_matcher.h +46 -0
  52. data/ext/mongory_ext/mongory-core/src/matchers/inclusion_matcher.c +126 -0
  53. data/ext/mongory_ext/mongory-core/src/matchers/inclusion_matcher.h +46 -0
  54. data/ext/mongory_ext/mongory-core/src/matchers/literal_matcher.c +314 -0
  55. data/ext/mongory_ext/mongory-core/src/matchers/literal_matcher.h +97 -0
  56. data/ext/mongory_ext/mongory-core/src/matchers/matcher.c +252 -0
  57. data/ext/mongory_ext/mongory-core/src/matchers/matcher_explainable.c +79 -0
  58. data/ext/mongory_ext/mongory-core/src/matchers/matcher_explainable.h +23 -0
  59. data/ext/mongory_ext/mongory-core/src/matchers/matcher_traversable.c +60 -0
  60. data/ext/mongory_ext/mongory-core/src/matchers/matcher_traversable.h +23 -0
  61. data/ext/mongory_ext/mongory_ext.c +683 -0
  62. data/lib/generators/mongory/install/install_generator.rb +42 -0
  63. data/lib/generators/mongory/install/templates/initializer.rb.erb +83 -0
  64. data/lib/generators/mongory/matcher/matcher_generator.rb +56 -0
  65. data/lib/generators/mongory/matcher/templates/matcher.rb.erb +92 -0
  66. data/lib/generators/mongory/matcher/templates/matcher_spec.rb.erb +17 -0
  67. data/lib/mongory/c_query_builder.rb +44 -0
  68. data/lib/mongory/converters/abstract_converter.rb +111 -0
  69. data/lib/mongory/converters/condition_converter.rb +64 -0
  70. data/lib/mongory/converters/converted.rb +81 -0
  71. data/lib/mongory/converters/data_converter.rb +37 -0
  72. data/lib/mongory/converters/key_converter.rb +87 -0
  73. data/lib/mongory/converters/value_converter.rb +52 -0
  74. data/lib/mongory/converters.rb +8 -0
  75. data/lib/mongory/matchers/abstract_matcher.rb +219 -0
  76. data/lib/mongory/matchers/abstract_multi_matcher.rb +124 -0
  77. data/lib/mongory/matchers/and_matcher.rb +72 -0
  78. data/lib/mongory/matchers/array_record_matcher.rb +93 -0
  79. data/lib/mongory/matchers/elem_match_matcher.rb +55 -0
  80. data/lib/mongory/matchers/eq_matcher.rb +46 -0
  81. data/lib/mongory/matchers/every_matcher.rb +56 -0
  82. data/lib/mongory/matchers/exists_matcher.rb +53 -0
  83. data/lib/mongory/matchers/field_matcher.rb +147 -0
  84. data/lib/mongory/matchers/gt_matcher.rb +41 -0
  85. data/lib/mongory/matchers/gte_matcher.rb +41 -0
  86. data/lib/mongory/matchers/hash_condition_matcher.rb +62 -0
  87. data/lib/mongory/matchers/in_matcher.rb +68 -0
  88. data/lib/mongory/matchers/literal_matcher.rb +121 -0
  89. data/lib/mongory/matchers/lt_matcher.rb +41 -0
  90. data/lib/mongory/matchers/lte_matcher.rb +41 -0
  91. data/lib/mongory/matchers/ne_matcher.rb +38 -0
  92. data/lib/mongory/matchers/nin_matcher.rb +68 -0
  93. data/lib/mongory/matchers/not_matcher.rb +40 -0
  94. data/lib/mongory/matchers/or_matcher.rb +68 -0
  95. data/lib/mongory/matchers/present_matcher.rb +55 -0
  96. data/lib/mongory/matchers/regex_matcher.rb +80 -0
  97. data/lib/mongory/matchers/size_matcher.rb +54 -0
  98. data/lib/mongory/matchers.rb +176 -0
  99. data/lib/mongory/mongoid.rb +19 -0
  100. data/lib/mongory/query_builder.rb +257 -0
  101. data/lib/mongory/query_matcher.rb +93 -0
  102. data/lib/mongory/query_operator.rb +28 -0
  103. data/lib/mongory/rails.rb +15 -0
  104. data/lib/mongory/utils/context.rb +48 -0
  105. data/lib/mongory/utils/debugger.rb +125 -0
  106. data/lib/mongory/utils/rails_patch.rb +22 -0
  107. data/lib/mongory/utils/singleton_builder.rb +31 -0
  108. data/lib/mongory/utils.rb +76 -0
  109. data/lib/mongory/version.rb +5 -0
  110. data/lib/mongory.rb +123 -0
  111. data/lib/mongory_ext.bundle +0 -0
  112. data/mongory.gemspec +50 -0
  113. data/scripts/build_with_core.sh +292 -0
  114. data/sig/mongory.rbs +4 -0
  115. metadata +164 -0
data/README.md ADDED
@@ -0,0 +1,488 @@
1
+ # Mongory-rb
2
+
3
+ A Mongo-like in-memory query DSL for Ruby.
4
+
5
+ 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
+ ## Table of Contents
8
+
9
+ - Overview & Positioning
10
+ - [Positioning](#positioning)
11
+ - Getting Started
12
+ - [Requirements](#requirements)
13
+ - [Installation & Quick Start](#installation--quick-start)
14
+ - [Integration with MongoDB](#integration-with-mongodb)
15
+ - Usage & Concepts
16
+ - [Creating Custom Matchers](#creating-custom-matchers)
17
+ - [Core Concepts & API Reference](#core-concepts--api-reference)
18
+ - [Handling Dots in Field Names](docs/field_names.md)
19
+ - [Advanced Usage](docs/advanced_usage.md)
20
+ - [Debugging](#debugging)
21
+ - [Clang Bridge (C Extension)](docs/clang_bridge.md)
22
+ - Performance
23
+ - [Performance & Benchmarks](docs/performance.md)
24
+ - [Supported Operators](#supported-operators)
25
+ - Guides
26
+ - [Best Practices](#best-practices)
27
+ - [Limitations](#limitations)
28
+ - [FAQ](#faq)
29
+ - [Troubleshooting](#troubleshooting)
30
+ - [Migration Guide](docs/migration.md)
31
+ - Project
32
+ - [Contributing](#contributing)
33
+ - [Code of Conduct](#code-of-conduct)
34
+ - [License](#license)
35
+
36
+ ## Requirements
37
+
38
+ - Ruby >= 2.6.0
39
+ - No external database required
40
+
41
+ ## Installation & Quick Start
42
+
43
+ ### Installation
44
+ Install manually:
45
+ ```bash
46
+ gem install mongory
47
+ ```
48
+
49
+ Or add to your Gemfile:
50
+ ```ruby
51
+ gem 'mongory'
52
+ ```
53
+
54
+ #### Before installing: install build tools
55
+
56
+ Mongory ships with an optional native extension. Before installing the gem, make sure your system has a C build toolchain (gcc/clang and make). Install the toolchain with the following commands for your platform:
57
+
58
+ - Debian/Ubuntu (including ruby:*-slim base images)
59
+ ```bash
60
+ apt-get update && apt-get install -y build-essential
61
+ ```
62
+
63
+ - Alpine
64
+ ```bash
65
+ apk add --no-cache build-base
66
+ ```
67
+
68
+ - CentOS/RHEL
69
+ ```bash
70
+ yum groupinstall -y "Development Tools"
71
+ ```
72
+
73
+ - Fedora
74
+ ```bash
75
+ dnf groupinstall -y "Development Tools"
76
+ ```
77
+
78
+ - Amazon Linux
79
+ ```bash
80
+ yum groupinstall -y "Development Tools"
81
+ ```
82
+
83
+ - macOS
84
+ ```bash
85
+ xcode-select --install
86
+ ```
87
+
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
+ ### Basic Usage
102
+ ```ruby
103
+ records = [
104
+ { 'name' => 'Jack', 'age' => 18, 'gender' => 'M' },
105
+ { 'name' => 'Jill', 'age' => 15, 'gender' => 'F' },
106
+ { 'name' => 'Bob', 'age' => 21, 'gender' => 'M' },
107
+ { 'name' => 'Mary', 'age' => 18, 'gender' => 'F' }
108
+ ]
109
+
110
+ # Basic query with conditions
111
+ result = records.mongory
112
+ .where(:age.gte => 18)
113
+ .or({ :name => /J/ }, { :name.eq => 'Bob' })
114
+
115
+ # Using limit to restrict results
116
+ # Note: limit executes immediately and affects subsequent conditions
117
+ limited = records.mongory
118
+ .limit(2) # Only process first 2 records
119
+ .where(:age.gte => 18) # Conditions apply to limited set
120
+ ```
121
+
122
+ ### C Extension (Optional but Recommended)
123
+
124
+ Mongory-rb includes an optional high-performance C extension powered by [mongory-core](https://github.com/mongoryhq/mongory-core):
125
+
126
+ **System Dependencies:**
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)
129
+
130
+ **Installation:**
131
+ ```bash
132
+ # macOS
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
140
+ ```
141
+
142
+ The C extension provides significant performance improvements for large datasets. If not available, Mongory-rb automatically falls back to pure Ruby implementation.
143
+
144
+ 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.
145
+
146
+ ## Positioning
147
+
148
+ Mongory is designed to serve two types of users:
149
+
150
+ 1. For MongoDB users:
151
+ - Seamless integration with familiar query syntax
152
+ - Extends query capabilities for non-indexed fields
153
+ - No additional learning cost
154
+
155
+ 2. For non-MongoDB users:
156
+ - Initial learning cost for MongoDB-style syntax
157
+ - Long-term benefits:
158
+ - Improved code readability
159
+ - Better development efficiency
160
+ - Lower maintenance costs
161
+ - Ideal for teams valuing code quality and maintainability
162
+
163
+ ### Integration with MongoDB
164
+
165
+ Mongory is designed to complement MongoDB, not replace it. Here's how to use them together:
166
+
167
+ 1. Use MongoDB for:
168
+ - Queries with indexes
169
+ - Persistent data operations
170
+ - Large-scale data processing
171
+
172
+ 2. Use Mongory for:
173
+ - Queries without indexes
174
+ - Complex in-memory calculations
175
+ - Temporary data filtering needs
176
+
177
+ Example:
178
+ ```ruby
179
+ # First use MongoDB for indexed queries
180
+ users = User.where(status: 'active') # Uses MongoDB index
181
+
182
+ # Then use Mongory for non-indexed fields
183
+ active_users = users.mongory
184
+ .where(:last_login.gte => 1.week.ago) # No index on last_login
185
+ .where(:tags.elem_match => { :name => 'ruby' }) # Complex array query
186
+ ```
187
+
188
+ ### Creating Custom Matchers
189
+ #### Using the Generator
190
+
191
+ You can generate a new matcher using:
192
+
193
+ ```bash
194
+ rails g mongory:matcher class_in
195
+ ```
196
+
197
+ This will:
198
+ 1. Create a new matcher file at `lib/mongory/matchers/class_in_matcher.rb`
199
+ 2. Create a spec file at `spec/mongory/matchers/class_in_matcher_spec.rb`
200
+ 3. Update `config/initializers/mongory.rb` to require the new matcher
201
+
202
+ The generated matcher will:
203
+ - Be named `ClassInMatcher`
204
+ - Register the operator as `$classIn`
205
+ - Be available as `:class_in` in queries
206
+
207
+ Example usage of the generated matcher:
208
+ ```ruby
209
+ records.mongory.where(:value.class_in => [Integer, String])
210
+ ```
211
+
212
+ #### Manual Creation
213
+
214
+ If you prefer to create matchers manually, here's an example:
215
+
216
+ ```ruby
217
+ class ClassInMatcher < Mongory::Matchers::AbstractMatcher
218
+ def match(subject)
219
+ @condition.any? { |klass| subject.is_a?(klass) }
220
+ end
221
+
222
+ def check_validity!
223
+ raise TypeError, '$classIn needs an array.' unless @condition.is_a?(Array)
224
+ @condition.each do |klass|
225
+ raise TypeError, '$classIn needs an array of class.' unless klass.is_a?(Class)
226
+ end
227
+ end
228
+ end
229
+
230
+ Mongory::Matchers.register(:class_in, '$classIn', ClassInMatcher)
231
+
232
+ [{a: 1}].mongory.where(:a.class_in => [Integer]).first
233
+ # => { a: 1 }
234
+ ```
235
+
236
+ You can define any matcher behavior and attach it to a `$operator` of your choice.
237
+ Matchers can be composed, validated, and traced just like built-in ones.
238
+
239
+ ## Core Concepts & API Reference
240
+ #### Registering Models
241
+
242
+ To allow calling `.mongory` on collections, use `register`:
243
+
244
+ ```ruby
245
+ Mongory.register(Array)
246
+ Mongory.register(ActiveRecord::Relation)
247
+ User.where(status: 'active').mongory.where(:age.gte => 18, :name.regex => "^S.+")
248
+ ```
249
+
250
+ 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`.
253
+
254
+ | Method | Description | Example |
255
+ |--------|-------------|---------|
256
+ | `where` | Adds a condition to filter records | `where(age: { :$gte => 18 })` |
257
+ | `not` | Adds a negated condition | `not(age: { :$lt => 18 })` |
258
+ | `and` | Combines conditions with `$and` | `and({ age: { :$gte => 18 } }, { name: /J/ })` |
259
+ | `or` | Combines conditions with `$or` | `or({ age: { :$gte => 18 } }, { name: /J/ })` |
260
+ | `any_of` | Combines conditions with `$or` inside an `$and` block | `any_of({ age: { :$gte => 18 } }, { name: /J/ })` |
261
+ | `in` | Checks if a value is in a set | `in(age: [18, 19, 20])` |
262
+ | `nin` | Checks if a value is not in a set | `nin(age: [18, 19, 20])` |
263
+ | `limit` | Limits the number of records returned. This method executes immediately and affects subsequent conditions. | `limit(2)` |
264
+ | `pluck` | Extracts selected fields from matching records | `pluck(:name)` |
265
+ | `with_context` | Sets a custom context for the query. Useful for controlling data conversion and sharing configuration across matchers. | `with_context(merchant: merchant)` |
266
+
267
+ #### Context Configuration
268
+
269
+ The `with_context` method allows you to customize the query execution environment:
270
+
271
+ ```ruby
272
+ # Share configuration across matchers
273
+ records.mongory
274
+ .with_context(custom_option: true)
275
+ .where(:status => 'active')
276
+ .where(:age.gte => 18)
277
+ ```
278
+
279
+ This will share a mutatable, but stable context object to all matchers in matcher tree.
280
+ To get your custom option, using `@context.config` in your custom matcher.
281
+
282
+ ## Debugging
283
+
284
+ You can use `explain` to visualize the matcher tree structure:
285
+ ```ruby
286
+ records = [
287
+ { name: 'John', age: 25, status: 'active' },
288
+ { name: 'Jane', age: 30, status: 'inactive' }
289
+ ]
290
+
291
+ query = records.mongory
292
+ .where(:age.gte => 18)
293
+ .any_of(
294
+ { :status => 'active' },
295
+ { :name.regex => /^J/ }
296
+ )
297
+
298
+ query.explain
299
+ ```
300
+ Output:
301
+ ```
302
+ And: {"age"=>{"$gte"=>18}, "$or"=>[{"status"=>"active"}, {"name"=>{"$regex"=>/^J/}}]}
303
+ ├─ Field: "age" to match: {"$gte"=>18}
304
+ │ └─ Gte: 18
305
+ └─ Or: [{"status"=>"active"}, {"name"=>{"$regex"=>/^J/}}]
306
+ ├─ Field: "status" to match: "active"
307
+ │ └─ Eq: "active"
308
+ └─ Field: "name" to match: {"$regex"=>/^J/}
309
+ └─ Regex: /^J/
310
+ ```
311
+
312
+ This helps you understand how your query is being processed and can be useful for debugging complex conditions.
313
+
314
+ Or use the debugger for detailed matching process:
315
+ ```ruby
316
+ # Enable debugging
317
+ Mongory.debugger.enable
318
+
319
+ # Execute your query
320
+ query = Mongory.build_query(users).where(age: { :$gt => 18 })
321
+ query.each do |user|
322
+ puts user
323
+ end
324
+
325
+ # Display the debug trace
326
+ Mongory.debugger.display
327
+ ```
328
+
329
+ The debug output will show detailed matching process with full class names:
330
+ ```
331
+ QueryMatcher Matched, condition: {"age"=>{"$gt"=>18}}, record: {"age"=>25}
332
+ AndMatcher Matched, condition: {"age"=>{"$gt"=>18}}, record: {"age"=>25}
333
+ FieldMatcher Matched, condition: {"$gt"=>18}, field: "age", record: {"age"=>25}
334
+ GtMatcher Matched, condition: 18, record: 25
335
+ ```
336
+
337
+ The debug output includes:
338
+ - The matcher tree structure with full class names
339
+ - Each matcher's condition and record value
340
+ - Color-coded results (green for matched, red for mismatched, purple for errors)
341
+ - Field names highlighted in gray background
342
+ - Detailed matching process for each record
343
+
344
+ ### Supported Operators
345
+
346
+ | Category | Operators |
347
+ |--------------|-------------------------------------|
348
+ | Comparison | `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte` |
349
+ | Set | `$in`, `$nin` |
350
+ | Boolean | `$and`, `$or`, `$not` |
351
+ | Pattern | `$regex` |
352
+ | Presence | `$exists`, `$present` |
353
+ | Nested Match | `$elemMatch`, `$every` |
354
+
355
+ Note: Some operators are Mongory-specific and not available in MongoDB:
356
+ - `$present`: Checks if a field is considered "present" (not nil, not empty, not KEY_NOT_FOUND)
357
+ - Similar to `$exists` but evaluates truthiness of the value
358
+ - Example: `where(:name.present => true)`
359
+ - `$every`: Checks if all elements in an array match the given condition
360
+ - Similar to `$elemMatch` but requires all elements to match
361
+ - At least one element in an array, or returns false
362
+ - Example: `where(:tags.every => { :priority.gt => 5 })`
363
+
364
+ Example:
365
+ ```ruby
366
+ # $present: Check if field is present (not nil, not empty)
367
+ records.mongory.where(:name.present => true) # name is present
368
+ records.mongory.where(:name.present => false) # name is not present
369
+
370
+ # $every: Check if all array elements match condition
371
+ records.mongory.where(:tags.every => { :priority.gt => 5 }) # all tags have priority > 5
372
+ ```
373
+
374
+ ## FAQ
375
+
376
+ ### Q: How does Mongory compare to MongoDB?
377
+ A: Mongory provides similar query syntax but operates entirely in memory. It's ideal for:
378
+ - Small to medium datasets
379
+ - Complex in-memory filtering
380
+ - Testing MongoDB-like queries without a database
381
+
382
+ ### Q: Can I use Mongory with large datasets?
383
+ A: Yes, but consider:
384
+ - Memory usage
385
+ - Query complexity
386
+ - Caching strategies
387
+ - Using `limit` early in the chain
388
+
389
+ ### Q: How do I handle errors?
390
+ ```ruby
391
+ begin
392
+ result = records.mongory.where(invalid: :condition)
393
+ rescue Mongory::Error => e
394
+ # Handle error
395
+ end
396
+ ```
397
+
398
+ ## Troubleshooting
399
+
400
+ 1. **Debugging Queries**
401
+ ```ruby
402
+ Mongory.debugger.enable
403
+ records.mongory.where(:age => 18).to_a
404
+ Mongory.debugger.display
405
+ Mongory.debugger.disable
406
+ ```
407
+
408
+ 2. **Common Issues**
409
+ - Symbol snippets not working? Call `Mongory.enable_symbol_snippets!`
410
+ - Complex queries slow? Use `explain` to analyze
411
+ - Memory issues? Consider pagination or streaming
412
+
413
+ ## Best Practices
414
+
415
+ 1. **Query Composition**
416
+ ```ruby
417
+ # Good: Use method chaining
418
+ records.mongory
419
+ .where(:age.gte => 18)
420
+ .where(:status => 'active')
421
+ .limit(10)
422
+
423
+ # Bad: Avoid redundant query creation
424
+ query = records.mongory.where(:age.gte => 18)
425
+ query = query.where(:status => 'active') # Unnecessary
426
+ ```
427
+
428
+ 2. **Performance Tips**
429
+ ```ruby
430
+ # Use limit to restrict result set
431
+ records.mongory.limit(100).where(:age.gte => 18)
432
+
433
+ # Use fast mode for better performance
434
+ records.mongory.where(:age.gte => 18).fast
435
+
436
+ # Use explain to analyze complex queries
437
+ query = records.mongory.where(:$or => [...])
438
+ query.explain
439
+ ```
440
+
441
+ 3. **Code Organization**
442
+ ```ruby
443
+ # Encapsulate common queries as methods
444
+ class User
445
+ def active_adults
446
+ friends.mongory
447
+ .where(:age.gte => 18)
448
+ .where(:status => 'active')
449
+ end
450
+ end
451
+ ```
452
+
453
+ ## Limitations
454
+
455
+ 1. **Data Size**
456
+ - Suitable for small to medium datasets
457
+ - Large datasets may impact performance
458
+ - Proc-based implementation helps with memory usage
459
+ - Context system provides better resource management
460
+
461
+ 2. **Query Complexity**
462
+ - Complex queries may affect performance
463
+ - Not all MongoDB operators are supported
464
+ - Proc-based implementation improves complex query performance
465
+ - Context system allows better control over query execution
466
+
467
+ 3. **Memory Usage**
468
+ - All operations are performed in memory
469
+ - Consider memory constraints
470
+
471
+ ## Contributing
472
+
473
+ Contributions are welcome! Here's how you can help:
474
+
475
+ 1. **Fork the repository**.
476
+ 2. **Create a new branch** for each significant change.
477
+ 3. **Write tests** for your changes.
478
+ 4. **Send a pull request**.
479
+
480
+ Please ensure your code adheres to the project's style guide and that all tests pass before submitting.
481
+
482
+ ## Code of Conduct
483
+
484
+ Everyone interacting in the Mongory-rb project's codebases, issue trackers, chat rooms, and mailing lists is expected to follow the [code of conduct](https://github.com/mongoryhq/mongory-rb/blob/main/CODE_OF_CONDUCT.md).
485
+
486
+ ## License
487
+
488
+ MIT. See LICENSE file.
data/Rakefile ADDED
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rake/extensiontask'
4
+ require 'bundler/gem_tasks'
5
+ begin
6
+ require 'rspec/core/rake_task'
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ require 'rubocop/rake_task'
10
+ RuboCop::RakeTask.new
11
+ rescue LoadError
12
+ # When rspec or rubocop is not installed in the CI cross-build environment, the definition of the test task is skipped
13
+ end
14
+
15
+ # Add support for rake-compiler if available
16
+ begin
17
+ ENV['RUBY_CC_VERSION'] ||= '2.6.0:2.7.0:3.0.0:3.1.0:3.2.0:3.3.0'
18
+
19
+ spec = Gem::Specification.load('mongory.gemspec')
20
+
21
+ Rake::ExtensionTask.new('mongory_ext', spec) do |ext|
22
+ ext.lib_dir = 'lib'
23
+ ext.ext_dir = 'ext/mongory_ext'
24
+ ext.source_pattern = '*.c'
25
+ ext.gem_spec = spec
26
+ ext.cross_compile = true
27
+ ext.cross_platform = [
28
+ 'x86_64-linux',
29
+ 'aarch64-linux',
30
+ 'x86_64-darwin',
31
+ 'arm64-darwin',
32
+ # 'arm64-mingw-ucrt', # TODO: add this when we have a mingw-ucrt rake-compiler-dock image
33
+ 'x64-mingw32',
34
+ 'x64-mingw-ucrt',
35
+ 'x86_64-linux-musl',
36
+ 'aarch64-linux-musl'
37
+ ]
38
+ end
39
+
40
+ # Add tasks for building with submodule
41
+ namespace :submodule do
42
+ desc 'Initialize/update the mongory-core submodule'
43
+ task :init do
44
+ sh 'git submodule update --init --recursive'
45
+ end
46
+
47
+ desc 'Update the mongory-core submodule to latest'
48
+ task :update do
49
+ sh 'git submodule update --remote'
50
+ end
51
+
52
+ desc 'Build mongory-core submodule'
53
+ task :build do
54
+ core_dir = 'ext/mongory_ext/mongory-core'
55
+ if Dir.exist?(core_dir)
56
+ Dir.chdir(core_dir) do
57
+ if File.exist?('build.sh')
58
+ sh 'chmod +x build.sh && ./build.sh'
59
+ else
60
+ sh 'mkdir -p build && cd build && cmake .. && make'
61
+ end
62
+ end
63
+ else
64
+ puts 'mongory-core submodule not found. Run rake submodule:init first.'
65
+ end
66
+ end
67
+ end
68
+
69
+ desc 'Build the project (without standalone mongory-core build)'
70
+ task build_all: ['submodule:init', :compile]
71
+
72
+ desc 'Clean all build artifacts including submodule'
73
+ task clean_all: :clean do
74
+ sh 'rm -rf ext/mongory_ext/mongory-core/build' if Dir.exist?('ext/mongory_ext/mongory-core/build')
75
+ end
76
+ rescue LoadError
77
+ puts 'rake-compiler not available. Install it with: gem install rake-compiler'
78
+
79
+ # Fallback tasks without rake-compiler
80
+ desc 'Build the C extension manually'
81
+ task :compile do
82
+ Dir.chdir('ext/mongory_ext') do
83
+ sh 'ruby extconf.rb && make'
84
+ end
85
+ end
86
+
87
+ desc 'Clean the C extension manually'
88
+ task :clean do
89
+ Dir.chdir('ext/mongory_ext') do
90
+ sh 'make clean' if File.exist?('Makefile')
91
+ sh 'rm -f Makefile *.o foundations/*.o matchers/*.o mongory_ext.so'
92
+ end
93
+ end
94
+ end
95
+
96
+ # Custom build task using our build script
97
+ desc 'Build using the custom build script'
98
+ task :build_with_script do
99
+ sh 'scripts/build_with_core.sh'
100
+ end
101
+
102
+ desc 'Build in debug mode'
103
+ task :build_debug do
104
+ sh 'scripts/build_with_core.sh --debug'
105
+ end
106
+
107
+ task default: %i(spec rubocop)