type_balancer 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TypeBalancer
4
+ # Registry to memoize TypeExtractor per type_field in thread/request scope
5
+ class TypeExtractorRegistry
6
+ STORAGE_KEY = :type_balancer_extractors
7
+
8
+ def self.get(type_field)
9
+ cache[type_field] ||= TypeExtractor.new(type_field)
10
+ end
11
+
12
+ def self.clear!
13
+ Thread.current[STORAGE_KEY] = nil
14
+ end
15
+
16
+ def self.cache
17
+ Thread.current[STORAGE_KEY] ||= {}
18
+ end
19
+ end
20
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TypeBalancer
4
- VERSION = '0.1.3'
4
+ VERSION = '0.2.0'
5
5
  end
data/lib/type_balancer.rb CHANGED
@@ -1,11 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'type_balancer/version'
4
- require 'type_balancer/calculator'
5
4
  require_relative 'type_balancer/balancer'
6
5
  require_relative 'type_balancer/ratio_calculator'
7
6
  require_relative 'type_balancer/batch_processing'
8
- require 'type_balancer/position_calculator'
7
+ require_relative 'type_balancer/position_calculator'
8
+ require_relative 'type_balancer/type_extractor'
9
+ require_relative 'type_balancer/type_extractor_registry'
10
+ require_relative 'type_balancer/strategies/base_strategy'
11
+ require_relative 'type_balancer/strategies/sliding_window_strategy'
12
+ require_relative 'type_balancer/strategy_factory'
9
13
 
10
14
  module TypeBalancer
11
15
  class Error < StandardError; end
@@ -14,50 +18,62 @@ module TypeBalancer
14
18
  class EmptyCollectionError < Error; end
15
19
  class InvalidTypeError < Error; end
16
20
 
21
+ # Register default strategies
22
+ StrategyFactory.register(:sliding_window, Strategies::SlidingWindowStrategy)
23
+
17
24
  # Load Ruby implementations
18
25
  require_relative 'type_balancer/distribution_calculator'
19
26
  require_relative 'type_balancer/ordered_collection_manager'
20
- require_relative 'type_balancer/alternating_filler'
21
- require_relative 'type_balancer/sequential_filler'
27
+ require_relative 'type_balancer/type_extractor'
28
+ require_relative 'type_balancer/type_extractor_registry'
29
+ require_relative 'type_balancer/ratio_calculator'
30
+ require_relative 'type_balancer/position_calculator'
22
31
  require_relative 'type_balancer/distributor'
32
+ require_relative 'type_balancer/sequential_filler'
33
+ require_relative 'type_balancer/alternating_filler'
34
+ require_relative 'type_balancer/balancer'
35
+ require_relative 'type_balancer/batch_processing'
36
+ require_relative 'type_balancer/calculator'
23
37
 
24
38
  def self.calculate_positions(total_count:, ratio:, available_items: nil)
25
- Distributor.calculate_target_positions(
39
+ PositionCalculator.calculate_positions(
26
40
  total_count: total_count,
27
41
  ratio: ratio,
28
- available_positions: available_items
42
+ available_items: available_items
29
43
  )
30
44
  end
31
45
 
32
- def self.balance(items, type_field: :type, type_order: nil)
46
+ def self.balance(items, type_field: :type, type_order: nil, strategy: nil, **strategy_options)
33
47
  # Input validation
34
48
  raise EmptyCollectionError, 'Collection cannot be empty' if items.empty?
35
49
 
36
- # Extract and validate types
37
- types = extract_types(items, type_field)
38
- raise Error, "Invalid type field: #{type_field}" if types.empty?
39
-
40
- # Group items by type
41
- items.group_by { |item| extract_type(item, type_field) }
50
+ # Use centralized extractor
51
+ extractor = TypeExtractorRegistry.get(type_field)
52
+ begin
53
+ types = extractor.extract_types(items)
54
+ raise Error, "Invalid type field: #{type_field}" if types.empty?
55
+ rescue Error => e
56
+ raise Error, "Cannot access type field '#{type_field}': #{e.message}"
57
+ end
42
58
 
43
- # Initialize balancer with type order if provided
44
- balancer = Balancer.new(types, type_order: type_order)
59
+ # Create calculator with strategy options
60
+ calculator = Calculator.new(
61
+ items,
62
+ type_field: type_field,
63
+ types: type_order || types,
64
+ strategy: strategy,
65
+ **strategy_options
66
+ )
45
67
 
46
68
  # Balance items
47
- balancer.call(items)
69
+ calculator.call
48
70
  end
49
71
 
72
+ # Backward compatibility methods
50
73
  def self.extract_types(items, type_field)
51
- items.map { |item| extract_type(item, type_field) }.uniq
52
- end
53
-
54
- def self.extract_type(item, type_field)
55
- if item.is_a?(Hash)
56
- item[type_field] || item[type_field.to_s]
57
- else
58
- item.public_send(type_field)
59
- end
60
- rescue NoMethodError
61
- nil
74
+ TypeExtractorRegistry.get(type_field).extract_types(items)
75
+ rescue Error
76
+ # For backward compatibility, return array with nil for inaccessible type fields
77
+ [nil]
62
78
  end
63
79
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: type_balancer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carl Smith
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-04-28 00:00:00.000000000 Z
10
+ date: 2025-05-01 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  description: Balances types in collections by ensuring each type appears a similar
13
13
  number of times
@@ -43,6 +43,7 @@ files:
43
43
  - docs/calculate_positions.md
44
44
  - docs/quality.md
45
45
  - examples/balance_test_data.yml
46
+ - examples/large_scale_balance_test.rb
46
47
  - examples/quality.rb
47
48
  - lib/type_balancer.rb
48
49
  - lib/type_balancer/alternating_filler.rb
@@ -55,7 +56,12 @@ files:
55
56
  - lib/type_balancer/position_calculator.rb
56
57
  - lib/type_balancer/ratio_calculator.rb
57
58
  - lib/type_balancer/sequential_filler.rb
59
+ - lib/type_balancer/strategies.rb
60
+ - lib/type_balancer/strategies/base_strategy.rb
61
+ - lib/type_balancer/strategies/sliding_window_strategy.rb
62
+ - lib/type_balancer/strategy_factory.rb
58
63
  - lib/type_balancer/type_extractor.rb
64
+ - lib/type_balancer/type_extractor_registry.rb
59
65
  - lib/type_balancer/version.rb
60
66
  - type_balancer.gemspec
61
67
  homepage: https://github.com/llwebconsulting/type_balancer