foundries 0.1.4 → 0.1.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ee65151ab0ae7d4ef338d8dd8dec76fbf9f0746be24d99d814abdee8716c500a
4
- data.tar.gz: 9a298a3ca012affe264f0467240f7a44b46decb2227f88f9eb2ca27c28f8bfad
3
+ metadata.gz: 12575eccffc55ee4735cc1fe6cb981ed1109d252d45b6435ab4b8333862f2663
4
+ data.tar.gz: 5ecb9d8707258542b76ec9e72caf4daa3619d72c0dff8f4778163a880302aa3b
5
5
  SHA512:
6
- metadata.gz: 3b0eef54e2302dbda0dcc4ee7a9224c75e31f90b42498dabcdcac4a8e00318bc79bd34f343367cd2f03ab1cb969f72b01d4336c49ae4329eac5d7ebd136b64c1
7
- data.tar.gz: 60f6748284f995381cc16f26a816f7f1ac9fac581948bbd0bc553c4839caf4cd9df6634982930d9017f896bdbb8d5f3edd3072aca7be19f5b9ba1620479c3a27
6
+ metadata.gz: bb1eba53555fdf59b449b06cef7e973516341d2806ceed56d42d2e26a43e58be877bf6459cb551b71610acb6790a7f001a3761b5098b25245bb8d3c58be6805e
7
+ data.tar.gz: ef7bd963c7c76e978a96ffed6562358a47cbfdb0da52e873f9613b4aecb3bcc357a4143282b7ff743ddada31e142fcdb8bd395035fa99291dbef4b890d06f419
data/CHANGELOG.md CHANGED
@@ -5,19 +5,21 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](http://keepachangelog.com/)
6
6
  and this project adheres to [Semantic Versioning](http://semver.org/).
7
7
 
8
- ## [0.1.4] - 2026-03-16
8
+ ## [0.1.6] - 2026-08-25
9
9
 
10
- ### Added
10
+ ### Fixed
11
11
 
12
- - Recording module to analyze FactoryBot usage and suggest preset candidates (a294324)
13
- - parallel_tests support for Recording with per-worker output and merge rake task (8e59deb)
12
+ - Snapshot files are read and written in binary, so captured data with non-ASCII bytes round-trips (6a7d3a3)
13
+ - Restoring a snapshot no longer rewinds a sequence below its current value (df66836)
14
14
 
15
- ### Changed
15
+ ## [0.1.5] - 2026-08-12
16
16
 
17
- - README now covers Recording feature and parallel_tests support (69fa45b)
17
+ ### Changed
18
18
 
19
- ## [0.1.3] - 2026-03-13
19
+ - Development dependencies are locked (1129826)
20
20
 
21
- ### Added
21
+ ### Fixed
22
22
 
23
- - ancestor DSL and ancestors_for for path-based hierarchy building (7027e06)
23
+ - Snapshot no longer caches a partial tree when a preset writes to a table that already held rows (2e9f863)
24
+ - Lint under standard 1.56.0 (52be9eb)
25
+ - Releases write a SHA512 checksum again (813a2ae)
@@ -28,7 +28,7 @@ module Foundries
28
28
  end
29
29
 
30
30
  def self.display_tree(tree)
31
- tree.children.map(&:to_s).join(", ")
31
+ tree.children.join(", ")
32
32
  end
33
33
 
34
34
  private_class_method :preset_name, :display_tree
@@ -66,7 +66,7 @@ module Foundries
66
66
  if children.empty?
67
67
  name
68
68
  else
69
- "#{name} > [#{children.map(&:to_s).join(", ")}]"
69
+ "#{name} > [#{children.join(", ")}]"
70
70
  end
71
71
  end
72
72
  end
@@ -16,6 +16,10 @@ module Foundries
16
16
  @connection.select_value("SELECT NOT EXISTS (SELECT 1 FROM #{quoted(table_name)})")
17
17
  end
18
18
 
19
+ def count(table_name)
20
+ @connection.select_value("SELECT COUNT(*) FROM #{quoted(table_name)}")
21
+ end
22
+
19
23
  def capture(table_name, io)
20
24
  raw = @connection.raw_connection
21
25
  raw.copy_data("COPY #{quoted(table_name)} TO STDOUT") do
@@ -51,10 +55,14 @@ module Foundries
51
55
  )
52
56
  return unless seq
53
57
 
54
- max_id = @connection.select_value(
55
- "SELECT COALESCE(MAX(#{@connection.quote_column_name(pk)}), 0) FROM #{quoted(table_name)}"
58
+ # setval is not transactional, and rows can already be committed
59
+ # past the restored data (e.g. seeded outside this transaction), so
60
+ # the sequence must only ever move forward.
61
+ @connection.execute(
62
+ "SELECT setval(#{@connection.quote(seq)}, GREATEST(" \
63
+ "(SELECT COALESCE(MAX(#{@connection.quote_column_name(pk)}), 0) FROM #{quoted(table_name)}), " \
64
+ "(SELECT last_value FROM #{seq})))"
56
65
  )
57
- @connection.execute("SELECT setval(#{@connection.quote(seq)}, #{max_id})")
58
66
  end
59
67
 
60
68
  private
@@ -13,7 +13,11 @@ module Foundries
13
13
  end
14
14
 
15
15
  def empty?(table_name)
16
- @connection.select_value("SELECT COUNT(*) FROM #{quoted(table_name)}") == 0
16
+ count(table_name) == 0
17
+ end
18
+
19
+ def count(table_name)
20
+ @connection.select_value("SELECT COUNT(*) FROM #{quoted(table_name)}")
17
21
  end
18
22
 
19
23
  def capture(table_name, io)
@@ -25,18 +25,41 @@ module Foundries
25
25
 
26
26
  # Record which tables are empty before the preset block runs.
27
27
  # Only these tables will be captured after the block completes.
28
+ #
29
+ # The already-populated tables are counted too, so #capture can tell
30
+ # whether the preset wrote into one of them. A preset that only updates
31
+ # existing rows leaves the count unchanged and slips past this check;
32
+ # presets insert, so the count is a good enough signal.
28
33
  def record_empty_tables
29
- @capturable_tables = @adapter.table_names.select { |t| @adapter.empty?(t) }
34
+ @capturable_tables = []
35
+ @preexisting_counts = {}
36
+
37
+ @adapter.table_names.each do |table|
38
+ if @adapter.empty?(table)
39
+ @capturable_tables << table
40
+ else
41
+ @preexisting_counts[table] = @adapter.count(table)
42
+ end
43
+ end
30
44
  end
31
45
 
32
46
  def capture
47
+ spoiled = spoiled_tables
48
+ unless spoiled.empty?
49
+ warn "[Foundries] Not caching preset :#{@preset_name} — it wrote to " \
50
+ "#{spoiled.join(", ")}, which already held rows when it ran. " \
51
+ "Only tables the preset fills from empty can be snapshotted, so " \
52
+ "caching this would restore an incomplete tree."
53
+ return
54
+ end
55
+
33
56
  tables = @capturable_tables || @adapter.table_names
34
57
 
35
58
  tmp_dir = Pathname.new("#{cache_dir}.#{$$}.tmp")
36
59
  tmp_dir.mkpath
37
60
 
38
61
  tables.each do |table|
39
- tmp_dir.join("#{table}.dat").open("w") do |f|
62
+ tmp_dir.join("#{table}.dat").open("wb") do |f|
40
63
  @adapter.capture(table, f)
41
64
  end
42
65
  end
@@ -54,7 +77,7 @@ module Foundries
54
77
  next unless file.size > 0
55
78
 
56
79
  table = file.basename(".dat").to_s
57
- file.open("r") do |f|
80
+ file.open("rb") do |f|
58
81
  @adapter.restore(table, f)
59
82
  end
60
83
  @adapter.reset_sequence(table)
@@ -64,6 +87,17 @@ module Foundries
64
87
 
65
88
  private
66
89
 
90
+ # Pre-populated tables whose row count moved while the preset ran. Their
91
+ # new rows can't be told apart from the ones that were already there, so
92
+ # the snapshot would silently omit them.
93
+ def spoiled_tables
94
+ return [] unless @preexisting_counts
95
+
96
+ @preexisting_counts.filter_map do |table, count|
97
+ table unless @adapter.count(table) == count
98
+ end
99
+ end
100
+
67
101
  def cache_dir
68
102
  @cache_dir ||= Pathname.new(@storage_path).join(@preset_name)
69
103
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Foundries
4
- VERSION = "0.1.4"
5
- RELEASE_DATE = "2026-03-16"
4
+ VERSION = "0.1.6"
5
+ RELEASE_DATE = "2026-08-25"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foundries
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Dowd
@@ -93,7 +93,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
93
93
  - !ruby/object:Gem::Version
94
94
  version: '0'
95
95
  requirements: []
96
- rubygems_version: 4.0.3
96
+ rubygems_version: 4.0.16
97
97
  specification_version: 4
98
98
  summary: Declarative trees of related data using factory_bot
99
99
  test_files: []