type_balancer 0.1.0 → 0.1.3
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/.DS_Store +0 -0
- data/CHANGELOG.md +37 -3
- data/Dockerfile +3 -1
- data/Gemfile.lock +1 -1
- data/README.md +129 -9
- data/Rakefile +7 -29
- data/benchmark/end_to_end_benchmark.rb +6 -3
- data/benchmark_results/ruby3.2.8.txt +8 -8
- data/benchmark_results/ruby3.2.8_yjit.txt +13 -13
- data/benchmark_results/ruby3.3.7.txt +8 -8
- data/benchmark_results/ruby3.3.7_yjit.txt +13 -13
- data/benchmark_results/ruby3.4.2.txt +8 -8
- data/benchmark_results/ruby3.4.2_yjit.txt +13 -13
- data/docs/README.md +8 -0
- data/docs/balance.md +71 -0
- data/docs/benchmarks/README.md +57 -51
- data/docs/calculate_positions.md +87 -0
- data/docs/quality.md +101 -0
- data/examples/balance_test_data.yml +66 -0
- data/examples/quality.rb +230 -46
- data/lib/type_balancer/balancer.rb +71 -94
- data/lib/type_balancer/batch_processing.rb +35 -0
- data/lib/type_balancer/distributor.rb +26 -53
- data/lib/type_balancer/position_calculator.rb +61 -0
- data/lib/type_balancer/ratio_calculator.rb +91 -0
- data/lib/type_balancer/type_extractor.rb +29 -0
- data/lib/type_balancer/version.rb +1 -1
- data/lib/type_balancer.rb +36 -17
- metadata +12 -3
- data/sig/type_balancer.rbs +0 -85
@@ -2,16 +2,16 @@
|
|
2
2
|
+ bundle exec ruby -I lib benchmark/end_to_end_benchmark.rb
|
3
3
|
Ruby version: 3.4.2
|
4
4
|
RUBY_PLATFORM: aarch64-linux
|
5
|
-
YJIT enabled:
|
5
|
+
YJIT enabled: true
|
6
6
|
|
7
7
|
Running benchmarks...
|
8
8
|
|
9
9
|
Benchmarking Tiny Dataset (10 items)
|
10
|
-
ruby 3.4.2 (2025-02-15 revision d2930f8e7a) +PRISM [aarch64-linux]
|
10
|
+
ruby 3.4.2 (2025-02-15 revision d2930f8e7a) +YJIT +PRISM [aarch64-linux]
|
11
11
|
Warming up --------------------------------------
|
12
|
-
Ruby Implementation
|
12
|
+
Ruby Implementation 15.310k i/100ms
|
13
13
|
Calculating -------------------------------------
|
14
|
-
Ruby Implementation
|
14
|
+
Ruby Implementation 152.742k (±10.9%) i/s (6.55 μs/i) - 306.200k in 2.025235s
|
15
15
|
|
16
16
|
Distribution Stats:
|
17
17
|
Video: 4 (40.0%)
|
@@ -19,11 +19,11 @@ Image: 3 (30.0%)
|
|
19
19
|
Article: 3 (30.0%)
|
20
20
|
|
21
21
|
Benchmarking Small Dataset (100 items)
|
22
|
-
ruby 3.4.2 (2025-02-15 revision d2930f8e7a) +PRISM [aarch64-linux]
|
22
|
+
ruby 3.4.2 (2025-02-15 revision d2930f8e7a) +YJIT +PRISM [aarch64-linux]
|
23
23
|
Warming up --------------------------------------
|
24
|
-
Ruby Implementation
|
24
|
+
Ruby Implementation 2.912k i/100ms
|
25
25
|
Calculating -------------------------------------
|
26
|
-
Ruby Implementation
|
26
|
+
Ruby Implementation 32.388k (± 6.8%) i/s (30.88 μs/i) - 66.976k in 2.077301s
|
27
27
|
|
28
28
|
Distribution Stats:
|
29
29
|
Video: 34 (34.0%)
|
@@ -31,11 +31,11 @@ Image: 33 (33.0%)
|
|
31
31
|
Article: 33 (33.0%)
|
32
32
|
|
33
33
|
Benchmarking Medium Dataset (1000 items)
|
34
|
-
ruby 3.4.2 (2025-02-15 revision d2930f8e7a) +PRISM [aarch64-linux]
|
34
|
+
ruby 3.4.2 (2025-02-15 revision d2930f8e7a) +YJIT +PRISM [aarch64-linux]
|
35
35
|
Warming up --------------------------------------
|
36
|
-
Ruby Implementation
|
36
|
+
Ruby Implementation 368.000 i/100ms
|
37
37
|
Calculating -------------------------------------
|
38
|
-
Ruby Implementation
|
38
|
+
Ruby Implementation 3.646k (±14.2%) i/s (274.30 μs/i) - 10.672k in 3.009052s
|
39
39
|
|
40
40
|
Distribution Stats:
|
41
41
|
Video: 334 (33.4%)
|
@@ -43,11 +43,11 @@ Image: 333 (33.3%)
|
|
43
43
|
Article: 333 (33.3%)
|
44
44
|
|
45
45
|
Benchmarking Large Dataset (10000 items)
|
46
|
-
ruby 3.4.2 (2025-02-15 revision d2930f8e7a) +PRISM [aarch64-linux]
|
46
|
+
ruby 3.4.2 (2025-02-15 revision d2930f8e7a) +YJIT +PRISM [aarch64-linux]
|
47
47
|
Warming up --------------------------------------
|
48
|
-
Ruby Implementation
|
48
|
+
Ruby Implementation 42.000 i/100ms
|
49
49
|
Calculating -------------------------------------
|
50
|
-
Ruby Implementation
|
50
|
+
Ruby Implementation 424.261 (± 5.2%) i/s (2.36 ms/i) - 1.302k in 3.077566s
|
51
51
|
|
52
52
|
Distribution Stats:
|
53
53
|
Video: 3334 (33.34%)
|
data/docs/README.md
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
# Documentation Index
|
2
|
+
|
3
|
+
Welcome to the TypeBalancer documentation! Below you'll find links to all major documentation resources for the gem.
|
4
|
+
|
5
|
+
- [Quality Script](quality.md): Integration and example script for verifying gem functionality, with instructions for running locally or from other projects.
|
6
|
+
- [Balance Method Details](balance.md): In-depth documentation for the `TypeBalancer.balance` method, including arguments, usage, and edge cases.
|
7
|
+
- [Calculate Positions Method Details](calculate_positions.md): Detailed documentation for `TypeBalancer.calculate_positions`, with examples and caveats.
|
8
|
+
- [Benchmarks](benchmarks/README.md): Performance metrics and benchmarking methodology for the gem.
|
data/docs/balance.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# Detailed Documentation: `TypeBalancer.balance`
|
2
|
+
|
3
|
+
`TypeBalancer.balance` is the main method for distributing items of different types across a sequence, ensuring optimal spacing and respecting type ratios. It is highly configurable and supports custom type fields and type orderings.
|
4
|
+
|
5
|
+
## Method Signature
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
TypeBalancer.balance(items, type_field: :type, type_order: nil)
|
9
|
+
```
|
10
|
+
|
11
|
+
### Arguments
|
12
|
+
- `items` (Array<Hash>): The collection of items to balance. Each item should have a type field (default: `:type`).
|
13
|
+
- `type_field` (Symbol/String, optional): The key to use for extracting the type from each item. Default is `:type`.
|
14
|
+
- `type_order` (Array<String>, optional): An array specifying the desired order of types in the output. If omitted, the gem determines the order automatically.
|
15
|
+
|
16
|
+
## Return Value
|
17
|
+
- Returns a new array of items, balanced by type and spaced as evenly as possible.
|
18
|
+
- The output array will have the same length as the input.
|
19
|
+
|
20
|
+
## Usage Examples
|
21
|
+
|
22
|
+
### 1. Basic Balancing
|
23
|
+
```ruby
|
24
|
+
items = [
|
25
|
+
{ type: 'video', title: 'Video 1' },
|
26
|
+
{ type: 'image', title: 'Image 1' },
|
27
|
+
{ type: 'article', title: 'Article 1' },
|
28
|
+
{ type: 'article', title: 'Article 2' },
|
29
|
+
{ type: 'image', title: 'Image 2' },
|
30
|
+
{ type: 'video', title: 'Video 2' }
|
31
|
+
]
|
32
|
+
balanced = TypeBalancer.balance(items)
|
33
|
+
# => [ { type: 'article', ... }, { type: 'image', ... }, { type: 'video', ... }, ... ]
|
34
|
+
```
|
35
|
+
|
36
|
+
### 2. Custom Type Order
|
37
|
+
```ruby
|
38
|
+
# Prioritize images, then videos, then articles
|
39
|
+
balanced = TypeBalancer.balance(items, type_order: %w[image video article])
|
40
|
+
# => [ { type: 'image', ... }, { type: 'video', ... }, { type: 'article', ... }, ... ]
|
41
|
+
```
|
42
|
+
|
43
|
+
### 3. Custom Type Field
|
44
|
+
```ruby
|
45
|
+
items = [
|
46
|
+
{ category: 'video', title: 'Video 1' },
|
47
|
+
{ category: 'image', title: 'Image 1' },
|
48
|
+
{ category: 'article', title: 'Article 1' }
|
49
|
+
]
|
50
|
+
balanced = TypeBalancer.balance(items, type_field: :category)
|
51
|
+
# => [ { category: 'article', ... }, { category: 'image', ... }, { category: 'video', ... } ]
|
52
|
+
```
|
53
|
+
|
54
|
+
### 4. Handling Missing Types
|
55
|
+
If a type in `type_order` is not present in the input, it is simply ignored in the output order.
|
56
|
+
|
57
|
+
### 5. Edge Cases
|
58
|
+
- **Empty Input:** Raises an exception (`Collection cannot be empty`).
|
59
|
+
- **Single Type:** All items are returned in their original order.
|
60
|
+
- **Missing Type Field:** If an item is missing the type field, it is ignored or may cause an error depending on context.
|
61
|
+
|
62
|
+
## Notes and Caveats
|
63
|
+
- The method is deterministic: the same input will always produce the same output.
|
64
|
+
- The `type_order` argument must be an array of strings matching the type values in your items.
|
65
|
+
- If you use a custom `type_field`, ensure all items have that field.
|
66
|
+
- The method does not mutate the input array.
|
67
|
+
|
68
|
+
## See Also
|
69
|
+
- [README.md](../README.md) for general usage
|
70
|
+
- [Quality Script Documentation](quality.md) for integration tests and examples
|
71
|
+
- [Detailed Position Calculation Documentation](calculate_positions.md)
|
data/docs/benchmarks/README.md
CHANGED
@@ -21,27 +21,27 @@ Each benchmark test evaluates performance across different collection sizes, fro
|
|
21
21
|
1. Tiny Dataset (Content Widget):
|
22
22
|
- Total Items: 10
|
23
23
|
- Distribution: Video (40%), Image (30%), Article (30%)
|
24
|
-
- Processing Time: ~
|
24
|
+
- Processing Time: ~6-7 microseconds
|
25
25
|
|
26
26
|
2. Small Dataset (Content Feed):
|
27
27
|
- Total Items: 100
|
28
28
|
- Distribution: Video (34%), Image (33%), Article (33%)
|
29
|
-
- Processing Time: ~
|
29
|
+
- Processing Time: ~30-31 microseconds
|
30
30
|
|
31
31
|
3. Medium Dataset (Category Page):
|
32
32
|
- Total Items: 1,000
|
33
33
|
- Distribution: Video (33.4%), Image (33.3%), Article (33.3%)
|
34
|
-
- Processing Time: ~
|
34
|
+
- Processing Time: ~274-280 microseconds
|
35
35
|
|
36
36
|
4. Large Dataset (Site-wide Content):
|
37
37
|
- Total Items: 10,000
|
38
38
|
- Distribution: Video (33.34%), Image (33.33%), Article (33.33%)
|
39
|
-
- Processing Time: ~
|
39
|
+
- Processing Time: ~2.4-2.8 milliseconds
|
40
40
|
|
41
41
|
### Real-world Application
|
42
42
|
|
43
43
|
TypeBalancer is designed for practical use in content management and display systems:
|
44
|
-
- Process 10,000 items in under
|
44
|
+
- Process 10,000 items in under 3ms
|
45
45
|
- Maintain perfect distribution ratios
|
46
46
|
- Suitable for real-time web applications
|
47
47
|
- Efficient enough for on-the-fly content organization
|
@@ -60,62 +60,69 @@ TypeBalancer is designed for practical use in content management and display sys
|
|
60
60
|
|
61
61
|
| Metric | Tiny Dataset | Small Dataset | Medium Dataset | Large Dataset |
|
62
62
|
|--------|--------------|---------------|----------------|---------------|
|
63
|
-
| Speed (no YJIT) |
|
64
|
-
| Speed (YJIT) |
|
65
|
-
| Time/Op (no YJIT) |
|
66
|
-
| Time/Op (YJIT) |
|
67
|
-
| YJIT Impact | +
|
68
|
-
| Distribution Quality | Perfect | Excellent | Excellent |
|
63
|
+
| Speed (no YJIT) | 109.0K ops/sec | 21.9K ops/sec | 2.0K ops/sec | 264 ops/sec |
|
64
|
+
| Speed (YJIT) | 152.7K ops/sec | 32.4K ops/sec | 3.6K ops/sec | 424 ops/sec |
|
65
|
+
| Time/Op (no YJIT) | 9.18 μs | 45.71 μs | 498.96 μs | 3.79 ms |
|
66
|
+
| Time/Op (YJIT) | 6.55 μs | 30.88 μs | 274.30 μs | 2.36 ms |
|
67
|
+
| YJIT Impact | +40.1% | +48.0% | +80.0% | +60.6% |
|
68
|
+
| Distribution Quality | Perfect | Excellent | Excellent | Perfect |
|
69
69
|
|
70
70
|
### Ruby 3.3.7 Performance
|
71
71
|
|
72
72
|
| Metric | Tiny Dataset | Small Dataset | Medium Dataset | Large Dataset |
|
73
73
|
|--------|--------------|---------------|----------------|---------------|
|
74
|
-
| Speed (no YJIT) |
|
75
|
-
| Speed (YJIT) |
|
76
|
-
| Time/Op (no YJIT) |
|
77
|
-
| Time/Op (YJIT) |
|
78
|
-
| YJIT Impact | +44.
|
79
|
-
| Distribution Quality | Perfect | Excellent | Excellent |
|
74
|
+
| Speed (no YJIT) | 102.4K ops/sec | 20.8K ops/sec | 1.9K ops/sec | 245 ops/sec |
|
75
|
+
| Speed (YJIT) | 148.2K ops/sec | 31.2K ops/sec | 3.5K ops/sec | 394 ops/sec |
|
76
|
+
| Time/Op (no YJIT) | 9.77 μs | 48.08 μs | 526.32 μs | 4.08 ms |
|
77
|
+
| Time/Op (YJIT) | 6.75 μs | 32.05 μs | 277.78 μs | 2.54 ms |
|
78
|
+
| YJIT Impact | +44.7% | +50.0% | +84.2% | +60.8% |
|
79
|
+
| Distribution Quality | Perfect | Excellent | Excellent | Perfect |
|
80
80
|
|
81
81
|
### Ruby 3.2.8 Performance
|
82
82
|
|
83
83
|
| Metric | Tiny Dataset | Small Dataset | Medium Dataset | Large Dataset |
|
84
84
|
|--------|--------------|---------------|----------------|---------------|
|
85
|
-
| Speed (no YJIT) |
|
86
|
-
| Speed (YJIT) |
|
87
|
-
| Time/Op (no YJIT) | 13
|
88
|
-
| Time/Op (YJIT) |
|
89
|
-
| YJIT Impact | +
|
90
|
-
| Distribution Quality | Perfect | Excellent | Excellent |
|
85
|
+
| Speed (no YJIT) | 98.7K ops/sec | 19.2K ops/sec | 1.8K ops/sec | 223 ops/sec |
|
86
|
+
| Speed (YJIT) | 142.8K ops/sec | 30.1K ops/sec | 3.4K ops/sec | 356 ops/sec |
|
87
|
+
| Time/Op (no YJIT) | 10.13 μs | 52.08 μs | 555.56 μs | 4.48 ms |
|
88
|
+
| Time/Op (YJIT) | 7.00 μs | 33.22 μs | 280.70 μs | 2.81 ms |
|
89
|
+
| YJIT Impact | +44.7% | +56.8% | +88.9% | +59.6% |
|
90
|
+
| Distribution Quality | Perfect | Excellent | Excellent | Perfect |
|
91
91
|
|
92
92
|
## Analysis
|
93
93
|
|
94
94
|
### Performance Characteristics
|
95
95
|
|
96
96
|
1. Speed and Efficiency:
|
97
|
-
- Processes 10K items in ~
|
98
|
-
- Microsecond-level processing for small collections (
|
99
|
-
-
|
100
|
-
-
|
101
|
-
-
|
97
|
+
- Processes 10K items in ~2.4-4.5ms across all Ruby versions
|
98
|
+
- Microsecond-level processing for small collections (6-10μs)
|
99
|
+
- Sub-millisecond processing for medium collections (~275-555μs)
|
100
|
+
- Millisecond-level processing for large collections (2.4-4.5ms)
|
101
|
+
- YJIT provides substantial speedup across all dataset sizes (40-89% faster)
|
102
|
+
- Suitable for high-performance real-time applications
|
102
103
|
|
103
104
|
2. YJIT Impact:
|
104
|
-
- Most effective on
|
105
|
-
-
|
106
|
-
- Ruby 3.2
|
107
|
-
-
|
108
|
-
|
109
|
-
3.
|
110
|
-
-
|
111
|
-
-
|
105
|
+
- Most effective on medium datasets (up to 89% improvement)
|
106
|
+
- Consistent improvements across all dataset sizes
|
107
|
+
- Ruby 3.4.2 shows best absolute performance
|
108
|
+
- All versions benefit significantly from YJIT
|
109
|
+
|
110
|
+
3. Version Comparison:
|
111
|
+
- Ruby 3.4.2 with YJIT shows best overall performance
|
112
|
+
- Ruby 3.3.7 maintains strong second position
|
113
|
+
- Ruby 3.2.8 shows solid baseline performance
|
114
|
+
- Performance variance between versions is consistent
|
115
|
+
|
116
|
+
4. Distribution Quality:
|
117
|
+
- Perfect distribution in small and large datasets
|
118
|
+
- Highly accurate distribution in all dataset sizes
|
112
119
|
- Consistent quality across all Ruby versions and YJIT settings
|
113
120
|
|
114
121
|
### Scaling Characteristics
|
115
122
|
|
116
123
|
1. Dataset Size Impact:
|
117
|
-
-
|
118
|
-
- Sub-
|
124
|
+
- Near-linear performance scaling with size
|
125
|
+
- Sub-millisecond processing for datasets up to 1000 items
|
119
126
|
- Reliable performance characteristics
|
120
127
|
|
121
128
|
2. Memory Usage:
|
@@ -124,16 +131,15 @@ TypeBalancer is designed for practical use in content management and display sys
|
|
124
131
|
- Stable across different workloads
|
125
132
|
|
126
133
|
3. Distribution Quality:
|
127
|
-
- Maintains
|
128
|
-
- Improves with larger datasets
|
134
|
+
- Maintains perfect accuracy at all scales
|
129
135
|
- Consistent across implementations
|
130
136
|
|
131
137
|
## Use Cases
|
132
138
|
|
133
139
|
1. Content Management Systems:
|
134
|
-
- Homepage feeds (100s of items):
|
135
|
-
- Category pages (1000s of items): ~
|
136
|
-
- Site-wide content (10,000s of items): ~
|
140
|
+
- Homepage feeds (100s of items): ~31μs processing
|
141
|
+
- Category pages (1000s of items): ~275μs processing
|
142
|
+
- Site-wide content (10,000s of items): ~2.4ms processing
|
137
143
|
|
138
144
|
2. Real-time Applications:
|
139
145
|
- Widget content balancing: microsecond response
|
@@ -141,26 +147,26 @@ TypeBalancer is designed for practical use in content management and display sys
|
|
141
147
|
- Content reorganization: real-time capable
|
142
148
|
|
143
149
|
3. Batch Processing:
|
144
|
-
- Large collection processing: efficient
|
150
|
+
- Large collection processing: highly efficient
|
145
151
|
- Consistent performance characteristics
|
146
152
|
- Predictable resource usage
|
147
153
|
|
148
154
|
## Conclusions
|
149
155
|
|
150
156
|
1. Version Selection:
|
151
|
-
- Ruby 3.2
|
152
|
-
- All versions maintain
|
157
|
+
- Ruby 3.4.2 with YJIT shows optimal performance across all sizes
|
158
|
+
- All versions maintain perfect distribution quality
|
153
159
|
- Version choice can be based on other requirements
|
154
160
|
|
155
161
|
2. Production Readiness:
|
156
|
-
-
|
157
|
-
- Handles large datasets efficiently
|
158
|
-
-
|
162
|
+
- Exceptional performance for production workloads
|
163
|
+
- Handles large datasets very efficiently
|
164
|
+
- Suitable for high-frequency real-time processing
|
159
165
|
|
160
166
|
3. Future Outlook:
|
161
|
-
-
|
167
|
+
- Current performance exceeds most real-world requirements
|
162
168
|
- Focus on maintaining distribution quality
|
163
|
-
-
|
169
|
+
- Room for optimization in specific use cases
|
164
170
|
|
165
171
|
## Running the Benchmarks
|
166
172
|
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# Detailed Documentation: `TypeBalancer.calculate_positions`
|
2
|
+
|
3
|
+
`TypeBalancer.calculate_positions` is a utility method for determining the optimal positions for a given number of items (or a ratio of items) within a sequence of slots. This is useful for advanced scenarios where you want to control the distribution of a specific type or subset of items.
|
4
|
+
|
5
|
+
## Method Signature
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
TypeBalancer.calculate_positions(total_count:, ratio:, available_items: nil)
|
9
|
+
```
|
10
|
+
|
11
|
+
### Arguments
|
12
|
+
- `total_count` (Integer): The total number of slots in the sequence (e.g., the length of your feed or array).
|
13
|
+
- `ratio` (Float): The desired ratio of items to place (e.g., `0.3` for 30%).
|
14
|
+
- `available_items` (Array<Integer>, optional): An array of slot indices where placement is allowed. If omitted, all slots are considered available.
|
15
|
+
|
16
|
+
## Return Value
|
17
|
+
- Returns an array of integer indices representing the optimal positions for the items.
|
18
|
+
- The array will have a length close to `total_count * ratio`, rounded as appropriate.
|
19
|
+
- If `available_items` is provided, only those slots will be used.
|
20
|
+
|
21
|
+
## Usage Examples
|
22
|
+
|
23
|
+
### 1. Even Distribution
|
24
|
+
```ruby
|
25
|
+
# Place 3 items evenly in 10 slots
|
26
|
+
TypeBalancer.calculate_positions(total_count: 10, ratio: 0.3)
|
27
|
+
# => [0, 5, 9]
|
28
|
+
```
|
29
|
+
|
30
|
+
### 2. Restricting to Available Slots
|
31
|
+
```ruby
|
32
|
+
# Only use slots 0, 1, and 2
|
33
|
+
TypeBalancer.calculate_positions(total_count: 10, ratio: 0.5, available_items: [0, 1, 2])
|
34
|
+
# => [0, 1, 2]
|
35
|
+
```
|
36
|
+
|
37
|
+
### 3. Edge Cases
|
38
|
+
```ruby
|
39
|
+
# Single item
|
40
|
+
TypeBalancer.calculate_positions(total_count: 1, ratio: 1.0)
|
41
|
+
# => [0]
|
42
|
+
|
43
|
+
# No items
|
44
|
+
TypeBalancer.calculate_positions(total_count: 100, ratio: 0.0)
|
45
|
+
# => []
|
46
|
+
|
47
|
+
# All items
|
48
|
+
TypeBalancer.calculate_positions(total_count: 5, ratio: 1.0)
|
49
|
+
# => [0, 1, 2, 3, 4]
|
50
|
+
```
|
51
|
+
|
52
|
+
### 4. Precision with Small Ratios
|
53
|
+
```ruby
|
54
|
+
# Two positions in three slots
|
55
|
+
TypeBalancer.calculate_positions(total_count: 3, ratio: 0.67)
|
56
|
+
# => [0, 1]
|
57
|
+
|
58
|
+
# Single position in three slots
|
59
|
+
TypeBalancer.calculate_positions(total_count: 3, ratio: 0.34)
|
60
|
+
# => [0]
|
61
|
+
```
|
62
|
+
|
63
|
+
### 5. Available Items Edge Cases
|
64
|
+
```ruby
|
65
|
+
# Single target with multiple available positions
|
66
|
+
TypeBalancer.calculate_positions(total_count: 5, ratio: 0.2, available_items: [1, 2, 3])
|
67
|
+
# => [1]
|
68
|
+
|
69
|
+
# Two targets with multiple available positions
|
70
|
+
TypeBalancer.calculate_positions(total_count: 10, ratio: 0.2, available_items: [1, 3, 5])
|
71
|
+
# => [1, 5]
|
72
|
+
|
73
|
+
# Exact match of available positions
|
74
|
+
TypeBalancer.calculate_positions(total_count: 10, ratio: 0.3, available_items: [2, 4, 6])
|
75
|
+
# => [2, 4, 6]
|
76
|
+
```
|
77
|
+
|
78
|
+
## Notes and Caveats
|
79
|
+
- If `ratio` is 0 or `total_count` is 0, returns an empty array.
|
80
|
+
- If `ratio` is 1.0, returns all available slots.
|
81
|
+
- If `available_items` is provided and its length is less than the target count, all available items are returned.
|
82
|
+
- For very small or very large ratios, the method ensures at least one or all slots are used, respectively.
|
83
|
+
- The method is deterministic and will always return the same result for the same input.
|
84
|
+
|
85
|
+
## See Also
|
86
|
+
- [README.md](../README.md) for general usage
|
87
|
+
- [Quality Script Documentation](quality.md) for integration tests and examples
|
data/docs/quality.md
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
# Quality Script Documentation
|
2
|
+
|
3
|
+
The TypeBalancer gem includes a comprehensive quality check script located at `/examples/quality.rb`. This script serves multiple purposes:
|
4
|
+
|
5
|
+
1. **Documentation through Examples**: Demonstrates various use cases and features of the gem
|
6
|
+
2. **Quality Assurance**: Verifies that core functionality works as expected
|
7
|
+
3. **Integration Testing**: Tests how different components work together
|
8
|
+
|
9
|
+
## Running the Script
|
10
|
+
|
11
|
+
To run the quality script from the gem repository:
|
12
|
+
|
13
|
+
```bash
|
14
|
+
bundle exec ruby examples/quality.rb
|
15
|
+
```
|
16
|
+
|
17
|
+
### Running from Other Projects
|
18
|
+
|
19
|
+
You can run the quality script from any project that includes the TypeBalancer gem. This is useful for verifying the gem's behavior in your app or as part of CI for downstream projects.
|
20
|
+
|
21
|
+
**Example:**
|
22
|
+
|
23
|
+
```bash
|
24
|
+
bundle exec ruby /path/to/gems/type_balancer/examples/quality.rb
|
25
|
+
```
|
26
|
+
|
27
|
+
**How do I find the path to the gem?**
|
28
|
+
|
29
|
+
If you are not sure where the gem is installed, you can use Bundler to locate it:
|
30
|
+
|
31
|
+
```bash
|
32
|
+
bundle show type_balancer
|
33
|
+
```
|
34
|
+
|
35
|
+
This will print the path to the gem directory. The quality script is located in the `examples` subdirectory of that path. For example:
|
36
|
+
|
37
|
+
```bash
|
38
|
+
$ bundle show type_balancer
|
39
|
+
/Users/yourname/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/type_balancer-0.1.3
|
40
|
+
$ bundle exec ruby /Users/yourname/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/type_balancer-0.1.3/examples/quality.rb
|
41
|
+
```
|
42
|
+
|
43
|
+
- Ensure the gem is installed and available in your bundle.
|
44
|
+
- The script expects the test data file at `examples/balance_test_data.yml` (relative to the gem root).
|
45
|
+
- Output will be color-coded if your terminal supports ANSI colors.
|
46
|
+
|
47
|
+
## What it Tests
|
48
|
+
|
49
|
+
The script tests several key aspects of the TypeBalancer gem:
|
50
|
+
|
51
|
+
### 1. Basic Distribution
|
52
|
+
- Demonstrates how items are distributed across available slots
|
53
|
+
- Shows spacing calculations between positions
|
54
|
+
- Verifies edge cases (single item, no items, all items)
|
55
|
+
|
56
|
+
### 2. Robust Balance Method Tests
|
57
|
+
- Loads scenarios from a YAML file (`examples/balance_test_data.yml`)
|
58
|
+
- Tests `TypeBalancer.balance` with and without the `type_order` argument
|
59
|
+
- Checks type counts, custom order, and exception handling for empty input
|
60
|
+
- Prints a color-coded summary table for pass/fail counts
|
61
|
+
|
62
|
+
### 3. Content Feed Example
|
63
|
+
- Shows a real-world example of content type distribution
|
64
|
+
- Verifies position allocation for different content types (video, image, article)
|
65
|
+
- Checks distribution statistics and ratios
|
66
|
+
|
67
|
+
## Output Format
|
68
|
+
|
69
|
+
- Each section prints a color-coded summary table of passing and failing tests
|
70
|
+
- Failures and exceptions are highlighted in red; passes in green
|
71
|
+
- The final summary shows the total number of examples run and passed
|
72
|
+
- The script exits with status 0 if all tests pass, or 1 if any fail (CI-friendly)
|
73
|
+
|
74
|
+
## Using as a Development Tool
|
75
|
+
|
76
|
+
The quality script is particularly useful when:
|
77
|
+
1. Developing new features
|
78
|
+
2. Refactoring existing code
|
79
|
+
3. Verifying changes haven't broken core functionality
|
80
|
+
4. Understanding how different features work together
|
81
|
+
|
82
|
+
## Customizing/Extending the Script
|
83
|
+
|
84
|
+
- You can add new scenarios to `examples/balance_test_data.yml` to test additional cases or edge conditions.
|
85
|
+
- You may copy or extend the script for your own integration tests.
|
86
|
+
- The script can be adapted to accept a custom YAML path if needed (see comments in the script).
|
87
|
+
|
88
|
+
## Troubleshooting
|
89
|
+
|
90
|
+
- **Color output not working:** Ensure your terminal supports ANSI colors.
|
91
|
+
- **File not found:** Make sure `examples/balance_test_data.yml` exists and is accessible from the script's location.
|
92
|
+
- **Gem not found:** Ensure the TypeBalancer gem is installed and available in your bundle.
|
93
|
+
- **Path issues:** Use an absolute or correct relative path to the script when running from another project.
|
94
|
+
|
95
|
+
## Extending the Script
|
96
|
+
|
97
|
+
When adding new features to TypeBalancer, consider:
|
98
|
+
1. Adding relevant examples to the quality script
|
99
|
+
2. Including edge cases
|
100
|
+
3. Documenting expected behavior
|
101
|
+
4. Adding appropriate quality checks
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# Test data for TypeBalancer.balance integration tests
|
2
|
+
|
3
|
+
- name: Even distribution
|
4
|
+
items:
|
5
|
+
- { type: video, id: 1 }
|
6
|
+
- { type: video, id: 2 }
|
7
|
+
- { type: image, id: 3 }
|
8
|
+
- { type: image, id: 4 }
|
9
|
+
- { type: article, id: 5 }
|
10
|
+
- { type: article, id: 6 }
|
11
|
+
- { type: article, id: 7 }
|
12
|
+
- { type: article, id: 8 }
|
13
|
+
expected_type_counts:
|
14
|
+
video: 2
|
15
|
+
image: 2
|
16
|
+
article: 4
|
17
|
+
|
18
|
+
- name: Uneven distribution
|
19
|
+
items:
|
20
|
+
- { type: video, id: 1 }
|
21
|
+
- { type: image, id: 2 }
|
22
|
+
- { type: article, id: 3 }
|
23
|
+
- { type: article, id: 4 }
|
24
|
+
- { type: article, id: 5 }
|
25
|
+
- { type: article, id: 6 }
|
26
|
+
- { type: article, id: 7 }
|
27
|
+
- { type: article, id: 8 }
|
28
|
+
expected_type_counts:
|
29
|
+
video: 1
|
30
|
+
image: 1
|
31
|
+
article: 6
|
32
|
+
|
33
|
+
- name: Missing type (no images)
|
34
|
+
items:
|
35
|
+
- { type: video, id: 1 }
|
36
|
+
- { type: video, id: 2 }
|
37
|
+
- { type: article, id: 3 }
|
38
|
+
- { type: article, id: 4 }
|
39
|
+
- { type: article, id: 5 }
|
40
|
+
expected_type_counts:
|
41
|
+
video: 2
|
42
|
+
article: 3
|
43
|
+
|
44
|
+
- name: Custom type order
|
45
|
+
items:
|
46
|
+
- { type: video, id: 1 }
|
47
|
+
- { type: image, id: 2 }
|
48
|
+
- { type: article, id: 3 }
|
49
|
+
- { type: article, id: 4 }
|
50
|
+
- { type: image, id: 5 }
|
51
|
+
- { type: video, id: 6 }
|
52
|
+
- { type: article, id: 7 }
|
53
|
+
type_order: [image, video, article]
|
54
|
+
expected_first_type: image
|
55
|
+
|
56
|
+
- name: Edge case - empty
|
57
|
+
items: []
|
58
|
+
expected_type_counts: {}
|
59
|
+
|
60
|
+
- name: Edge case - single type
|
61
|
+
items:
|
62
|
+
- { type: article, id: 1 }
|
63
|
+
- { type: article, id: 2 }
|
64
|
+
- { type: article, id: 3 }
|
65
|
+
expected_type_counts:
|
66
|
+
article: 3
|