real_data_tests 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4834f12223bd9db7628aac93a05735a50cb61921313e2bf3b7d496e538dee461
4
- data.tar.gz: a8f6de663a143bc42469f953cc64eb49e2f9bc328f70990e885f87be2cac0305
3
+ metadata.gz: 1db1628bca035d2b5e5f402b39ceaabd5c78b5bf957cd7bcd7856e5819889334
4
+ data.tar.gz: 477319961d6795bd6005c8d5819cc7d26ded26ba46704cc3bc6ab2c5e701227e
5
5
  SHA512:
6
- metadata.gz: f00434ab2a006d6a4d0c92263ab160f1cfaf7fd38a110f36bd91280bf00d317708a136cbd2f54d2cc87151a3d9dba3130f4c58c21bb77bcedcc2135690a96179
7
- data.tar.gz: 645da39c079e2d1c7953f6588127b3144e98738dbaef82f720b11ea34dbd8dd9a582e0f6d70a0105df3a883a7e78ff2613d61d6c1cc24648e3849a9830b35ae3
6
+ metadata.gz: 30b473701dfd16d6337803a481caf8e358cf87c04877c076a66b2079da57bd68d0896d321c16c2cf1ecab1ce08b5179aba7f9845b0b28d58ecf9e67fb8d9f2b7
7
+ data.tar.gz: f0ea756e2384a7d43f5d40a36d018b569fc5abdba092f5635ade2d11ec1bf24a33c1ba219d1d983fbc7a918fbf842bd2a85436a644cc65fa40d2ea0fb07a923d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.3.0] - 2025-01-13
4
+ ### Added
5
+ - **Polymorphic Association Support**:
6
+ - RecordCollector now supports tracking and collecting records from polymorphic associations.
7
+ - Polymorphic `belongs_to`, `has_many`, and `has_one` associations are automatically detected and processed during data collection.
8
+ - Added tracking for polymorphic types in `@collection_stats` to provide detailed insights into polymorphic relationships.
9
+ - Graceful handling of missing records in polymorphic associations using error logging.
10
+
11
+ ### Fixed
12
+ - Improved error handling for `ActiveRecord::RecordNotFound` exceptions when loading polymorphic associations.
13
+ - Correctly initializes and updates association statistics for polymorphic associations in `@collection_stats`.
14
+
15
+ ## [0.2.1] - 2025-01-13
16
+ ### Fixed
17
+ - Fixed JSONB field handling to output '{}' instead of empty string for blank values
18
+ - Added test coverage for JSONB field handling in PgDumpGenerator
19
+
3
20
  ## [0.2.0] - 2025-01-13
4
21
  ### Added
5
22
  - New preset system for managing different test data configurations
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- real_data_tests (0.2.0)
4
+ real_data_tests (0.2.1)
5
5
  activerecord (>= 5.0)
6
6
  faker (~> 3.0)
7
7
  pg (>= 1.1)
data/README.md CHANGED
@@ -90,6 +90,37 @@ Rails.application.config.after_initialize do
90
90
  end
91
91
  ```
92
92
 
93
+ ## Polymorphic Association Support
94
+
95
+ Real Data Tests supports collecting records through polymorphic associations. This feature allows you to:
96
+ - Automatically detect and collect records for polymorphic `belongs_to`, `has_many`, and `has_one` associations.
97
+ - Track and report the types of records collected through polymorphic associations in detailed collection statistics.
98
+
99
+ ### Example
100
+ If your model includes a polymorphic association like this:
101
+
102
+ ```ruby
103
+ class Payment < ApplicationRecord
104
+ belongs_to :billable, polymorphic: true
105
+ end
106
+ ```
107
+
108
+ Real Data Tests will:
109
+ 1. Collect the associated `billable` records regardless of their type (e.g., `InsuranceCompany`, `Patient`).
110
+ 2. Include the `billable_type` in the collection statistics for transparency and reporting.
111
+
112
+ ### Configuration for Polymorphic Associations
113
+ Polymorphic associations are automatically handled based on your existing configuration. You can also explicitly include or limit polymorphic associations, like so:
114
+
115
+ ```ruby
116
+ RealDataTests.configure do |config|
117
+ config.include_associations_for 'Payment', :billable
118
+ config.limit_association 'Payment.billable', 5
119
+ end
120
+ ```
121
+
122
+ This ensures a robust and flexible way to handle even the most complex relationships in your data.
123
+
93
124
  ## Using Presets
94
125
 
95
126
  Real Data Tests allows you to define multiple configuration presets for different data extraction needs. This is particularly useful when you need different association rules and anonymization settings for different testing scenarios.
@@ -268,6 +299,22 @@ This is particularly useful when:
268
299
  - You want to collect an association from one model but not another
269
300
  - You need to maintain a clean separation of concerns in your test data
270
301
 
302
+ ### Polymorphic Associations
303
+ Polymorphic associations are fully supported. Include and configure them as needed:
304
+
305
+ ```ruby
306
+ RealDataTests.configure do |config|
307
+ config.include_associations_for 'Payment', :billable
308
+ end
309
+ ```
310
+
311
+ You can also limit or prevent reciprocal loading for polymorphic associations:
312
+
313
+ ```ruby
314
+ config.limit_association 'Payment.billable', 10
315
+ config.prevent_reciprocal 'Payment.billable'
316
+ ```
317
+
271
318
  ### Association Loading Control
272
319
 
273
320
  You can further refine how associations are loaded using limits and reciprocal prevention:
@@ -102,6 +102,11 @@ module RealDataTests
102
102
  @association_limits[path]
103
103
  end
104
104
 
105
+ def set_association_limit(model_name, association_name, limit)
106
+ path = "#{model_name}.#{association_name}"
107
+ @association_limits[path] = limit
108
+ end
109
+
105
110
  def prevent_reciprocal?(record_class, association_name)
106
111
  path = "#{record_class.name}.#{association_name}"
107
112
  @prevent_reciprocal_loading[path]
@@ -138,8 +138,14 @@ module RealDataTests
138
138
  value.to_s
139
139
  when :boolean
140
140
  value.to_s
141
- when :array, :json, :jsonb
142
- parse_and_format_special_type(value, column_info)
141
+ when :jsonb, :json
142
+ if value.blank?
143
+ "'{}'" # Return empty JSON object for blank JSONB/JSON fields
144
+ else
145
+ sanitize_string(value.is_a?(String) ? value : value.to_json)
146
+ end
147
+ when :array
148
+ parse_and_format_array(value, column_info[:sql_type])
143
149
  else
144
150
  if column_info[:array]
145
151
  parse_and_format_array(value, column_info[:sql_type])
@@ -1,11 +1,36 @@
1
1
  module RealDataTests
2
2
  class RecordCollector
3
+ attr_reader :collection_stats, :collected_records
4
+
3
5
  def initialize(record)
4
6
  @record = record
5
7
  @collected_records = Set.new
6
- @collection_stats = Hash.new { |h, k| h[k] = { count: 0, associations: Hash.new(0) } }
8
+ @collection_stats = {}
9
+
10
+ # Initialize stats for the record's class
11
+ @collection_stats[record.class.name] = {
12
+ count: 0,
13
+ associations: Hash.new(0),
14
+ polymorphic_types: {}
15
+ }
16
+
17
+ record.class.reflect_on_all_associations(:belongs_to).each do |assoc|
18
+ if assoc.polymorphic?
19
+ @collection_stats[record.class.name][:polymorphic_types][assoc.name.to_s] ||= Set.new
20
+ end
21
+ end
22
+
7
23
  @processed_associations = Set.new
8
24
  @association_path = []
25
+
26
+ puts "\nInitializing RecordCollector for #{record.class.name}##{record.id}"
27
+ record.class.reflect_on_all_associations(:belongs_to).each do |assoc|
28
+ if assoc.polymorphic?
29
+ type = record.public_send("#{assoc.name}_type")
30
+ id = record.public_send("#{assoc.name}_id")
31
+ puts "Found polymorphic belongs_to '#{assoc.name}' with type: #{type}, id: #{id}"
32
+ end
33
+ end
9
34
  end
10
35
 
11
36
  def collect
@@ -23,26 +48,54 @@ module RealDataTests
23
48
  def should_process_association?(record, association)
24
49
  association_key = "#{record.class.name}##{record.id}:#{association.name}"
25
50
  return false if @processed_associations.include?(association_key)
26
- @processed_associations.add(association_key)
27
51
 
28
- # Use the enhanced should_process_association? method
29
- return false unless RealDataTests.configuration.should_process_association?(record, association.name)
52
+ puts " Checking if should process: #{association_key}"
53
+ should_process = RealDataTests.configuration.should_process_association?(record, association.name)
54
+ puts " Configuration says: #{should_process}"
30
55
 
31
- # Check for prevented reciprocal loading
32
- if RealDataTests.configuration.prevent_reciprocal?(record.class, association.name)
33
- puts " Skipping prevented reciprocal association: #{association.name} on #{record.class.name}"
34
- return false
56
+ if should_process
57
+ @processed_associations.add(association_key)
58
+ if RealDataTests.configuration.prevent_reciprocal?(record.class, association.name)
59
+ puts " Skipping prevented reciprocal association: #{association.name} on #{record.class.name}"
60
+ return false
61
+ end
62
+ true
63
+ else
64
+ false
35
65
  end
36
-
37
- true
38
66
  end
39
67
 
40
68
  def collect_record(record)
41
69
  return if @collected_records.include?(record)
42
70
  return unless record # Guard against nil records
43
71
 
72
+ puts "\nCollecting record: #{record.class.name}##{record.id}"
44
73
  @collected_records.add(record)
74
+
75
+ # Ensure stats structure is initialized
76
+ @collection_stats[record.class.name] ||= { count: 0, associations: {}, polymorphic_types: {} }
45
77
  @collection_stats[record.class.name][:count] += 1
78
+
79
+ # Track types for polymorphic belongs_to associations
80
+ record.class.reflect_on_all_associations(:belongs_to).each do |assoc|
81
+ next unless assoc.polymorphic?
82
+
83
+ type = record.public_send("#{assoc.name}_type")
84
+ @collection_stats[record.class.name][:polymorphic_types][assoc.name.to_sym] ||= Set.new
85
+
86
+ begin
87
+ associated_record = record.public_send(assoc.name)
88
+ if associated_record
89
+ puts " Adding polymorphic type '#{type}' for #{assoc.name}"
90
+ @collection_stats[record.class.name][:polymorphic_types][assoc.name.to_sym] << associated_record.class.name
91
+ else
92
+ puts " Skipping polymorphic type for #{assoc.name} due to missing associated record"
93
+ end
94
+ rescue ActiveRecord::RecordNotFound => e
95
+ puts " Error loading polymorphic association #{assoc.name}: #{e.message}"
96
+ end
97
+ end
98
+
46
99
  collect_associations(record)
47
100
  end
48
101
 
@@ -54,14 +107,9 @@ module RealDataTests
54
107
  puts "Found #{associations.length} associations"
55
108
 
56
109
  associations.each do |association|
57
- should_process = RealDataTests.configuration.should_process_association?(record, association.name)
110
+ next unless should_process_association?(record, association)
58
111
 
59
- unless should_process
60
- puts " Skipping #{RealDataTests.configuration.association_filter_mode == :whitelist ? 'non-whitelisted' : 'blacklisted'} association: #{association.name} for #{record.class.name}"
61
- next
62
- end
63
-
64
- puts " Processing #{association.macro} association: #{association.name}"
112
+ puts " Processing #{association.macro} #{association.polymorphic? ? 'polymorphic ' : ''}association: #{association.name}"
65
113
  process_association(record, association)
66
114
  end
67
115
  end
@@ -71,7 +119,8 @@ module RealDataTests
71
119
  related_records = fetch_related_records(record, association)
72
120
  count = related_records.length
73
121
  puts " Found #{count} related #{association.name} records"
74
- @collection_stats[record.class.name][:associations][association.name] += count
122
+ @collection_stats[record.class.name][:associations][association.name.to_s] ||= 0
123
+ @collection_stats[record.class.name][:associations][association.name.to_s] += count
75
124
 
76
125
  related_records.each { |related_record| collect_record(related_record) }
77
126
  rescue => e
@@ -86,13 +135,14 @@ module RealDataTests
86
135
  when :has_many, :has_and_belongs_to_many
87
136
  relation = record.public_send(association.name)
88
137
 
89
- # Apply configured limit if it exists
90
138
  if limit = RealDataTests.configuration.get_association_limit(record.class, association.name)
91
- puts " Applying configured limit of #{limit} records for #{record.class.name}.#{association.name}"
139
+ puts " Applying configured limit of #{limit} records for #{record.class.name}.#{association.name}"
92
140
  relation = relation.limit(limit)
93
141
  end
94
142
 
95
- relation.loaded? ? relation.to_a : relation.load.to_a
143
+ records = relation.to_a
144
+ records = records[0...limit] if limit # Ensure in-memory limit as well
145
+ records
96
146
  else
97
147
  []
98
148
  end
@@ -103,12 +153,20 @@ module RealDataTests
103
153
  @collection_stats.each do |model, stats|
104
154
  puts "\n#{model}:"
105
155
  puts " Total records: #{stats[:count]}"
156
+
106
157
  if stats[:associations].any?
107
158
  puts " Associations:"
108
159
  stats[:associations].each do |assoc_name, count|
109
160
  puts " #{assoc_name}: #{count} records"
110
161
  end
111
162
  end
163
+
164
+ if stats[:polymorphic_types].any?
165
+ puts " Polymorphic Types:"
166
+ stats[:polymorphic_types].each do |assoc_name, types|
167
+ puts " #{assoc_name}: #{types.to_a.join(', ')}"
168
+ end
169
+ end
112
170
  end
113
171
  puts "\nTotal unique records collected: #{@collected_records.size}"
114
172
  puts "==============================\n"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RealDataTests
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: real_data_tests
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Dias
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-01-12 00:00:00.000000000 Z
11
+ date: 2025-01-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails