fixture_farm 1.0.1 → 1.1.1

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: af9b30c58737a261963da35795445aadfcb38efe2f3ed6d4fc8055f19399b0df
4
- data.tar.gz: 865e73ba12d379aba2b3f48a160ce421efe7d49a958db72f19b185d4d1045175
3
+ metadata.gz: d3a39068e7447367066d1947cf1f14d16366d10e88e91ae922ff3953b7667ff5
4
+ data.tar.gz: 7f28f26923b51368511ed5ef8c55531f2d405ba36d11ea802caedbc554fbcf0a
5
5
  SHA512:
6
- metadata.gz: 9c1950bc97570624b27f2f928ec82586816429e1f7a34378f5336c83d8d420180ad65093677744a4b6efa8e3cbc2b7a298631837dbb9ffc32d0c11941d372596
7
- data.tar.gz: 455b1430edd5fb21874e7cd906688ea55ea3e1753c0e02b13b2bcd051f2e68f17d02174f6b770cac9b15f212cef0923b52c3584f40f7b1d662957cff1a4c0cc3
6
+ metadata.gz: 84a8649ace5e14d8dd09b94cdde3d15f02ebd8bd602f706b48ec48f9bff9ab4ea0170fd26827886ee8b0595909d930ce16e61d21e422df9b9e9a7c545df248b7
7
+ data.tar.gz: d25f84374a79d33ee8ee117656c59e7523d4087dd3dc9065de7a540b882b5cd2fd9897e4aee3aa23af3a8f0eb43ee5168efb2d390dc12ecbde8b1a3b0fd22656
data/README.md CHANGED
@@ -55,7 +55,12 @@ include FixtureFarm::ActiveJobHook if defined?(FixtureFarm)
55
55
  Then start/stop recording using tasks:
56
56
 
57
57
  ```bash
58
- bundle exec fixture_farm record some_awesome_name_prefix
58
+ bundle exec fixture_farm record
59
+ # OR
60
+ bundle exec fixture_farm record name_prefix
61
+ # OR
62
+ bundle exec fixture_farm record name_prefix:replaces_name
63
+
59
64
  bundle exec fixture_farm status
60
65
  bundle exec fixture_farm stop
61
66
  ```
@@ -88,6 +93,25 @@ Assuming there was a parent fixture `dave` that didn't have any children, this t
88
93
 
89
94
  `record_fixtures` accepts optional name prefix, that applies to all new fixture names.
90
95
 
96
+ #### Fixture Name Replacement
97
+
98
+ `record_fixtures` also supports hash arguments for advanced fixture naming control:
99
+
100
+ ```ruby
101
+ # Replace 'client_1' with 'new_client' in fixture names, or use 'new_client' as prefix if not found
102
+ record_fixtures(new_client: :client_1) do
103
+ User.create!(name: 'Test User', email: 'test@example.com')
104
+ end
105
+ ```
106
+
107
+ This works in two ways:
108
+ - **Replacement**: If a generated fixture name contains `client_1`, it gets replaced with `new_client`
109
+ - **Prefixing**: If a generated fixture name doesn't contain `client_1`, it gets prefixed with `new_client_`
110
+
111
+ For example:
112
+ - A user fixture that would be named `client_1_user_1` becomes `new_client_user_1` (replacement)
113
+ - A user fixture that would be named `user_1` becomes `new_client_user_1` (prefixing)
114
+
91
115
  ### Automatic fixture naming
92
116
 
93
117
  Generated fixture names are based on the first `belongs_to` association of the model. E.g., if a new post fixtures belongs_to to a user fixture `bob`, the name is going to be `bob_post_1`.
@@ -100,25 +124,42 @@ FixtureFarm.low_priority_parent_model_for_naming = -> { _1.is_a?(TenantModel) }
100
124
 
101
125
  ### Attachment fixtures
102
126
 
103
- Rather than [manually crafting attachment fixtures](https://guides.rubyonrails.org/v8.0/active_storage_overview.html#adding-attachments-to-fixtures), we can get the gem do the leg work. Not only is this less boring, but it's also going to generate variant fixtures.
127
+ Rather than [manually crafting attachment fixtures](https://guides.rubyonrails.org/v8.0/active_storage_overview.html#adding-attachments-to-fixtures), we can get the gem to do the work. Not only is this less boring, but it's also going to generate variant fixtures.
104
128
 
105
- I'd also go as far as suggesting that attachment files for generated blobs should be checked into git just as the fixtures themselves are. To share them with the development environment (e.g. `rails db:fixtures:load`), let's store test attachment files in the same `./storage` directory used in development:
129
+ If we then check the generated blob files into git (along with the fixture files themselves), no attachment processing will be happening in tests or after `rails db:fixtures:load`.
106
130
 
107
- ```ruby
108
- # config/environments/test.rb
109
- config.active_storage.service = :local
131
+ We'll need a special storage service for the fixture blobs we want to keep versioned. For example:
132
+
133
+ ```yml
134
+ # config/storage.yml
135
+ test_fixtures:
136
+ service: Disk
137
+ root: <%= Rails.root.join("test/fixtures/files/active_storage_blobs") %>
110
138
  ```
111
139
 
112
- Now this test will not only generate attachments and variant fixtures, but also `git add` new attachment files. The old removed ones will show up in `git status`.
140
+ Now a test like the one below is either going to fail if some product fixtures have no attachments, or, if run with `GENERATE_FIXTURES=1`, is going to generate those attachment fixtures, their variant fixtures if needed, along with all the blob files tucked away in a separate (from regular throw away storage) folder that can be checked in:
113
141
 
114
142
  ```ruby
143
+ if ENV["GENERATE_FIXTURES"]
144
+ setup do
145
+ @original_queue_adapter = Rails.configuration.active_job.queue_adapter
146
+ # This is so that variants get generated and blobs analyzed
147
+ Rails.configuration.active_job.queue_adapter = :inline
148
+
149
+ @original_storage_service = ActiveStorage::Blob.service
150
+ ActiveStorage::Blob.service = ActiveStorage::Blob.services.fetch(:test_fixtures)
151
+ end
152
+
153
+ teardown do
154
+ Rails.configuration.active_job.queue_adapter = @original_queue_adapter
155
+ ActiveStorage::Blob.service = @original_storage_service
156
+ end
157
+ end
158
+
115
159
  test "product fixtures have images" do
116
160
  offending_records = Product.where.missing(:images_attachments)
117
161
 
118
162
  if ENV["GENERATE_FIXTURES"]
119
- # Makes generation idempotent
120
- `git restore --staged storage`
121
-
122
163
  record_fixtures do |recorder|
123
164
  ActiveStorage::Attachment.where(record_type: 'Product').destroy_all
124
165
 
@@ -128,13 +169,7 @@ test "product fixtures have images" do
128
169
  filename: "#{product.fixture_name}.jpg",
129
170
  content_type: "image/jpeg"
130
171
  )
131
- # This generates variants
132
- perform_enqueued_jobs
133
172
  end
134
-
135
- recorder.stop!
136
-
137
- `git add -f #{recorder.new_blob_file_paths.join(' ')}`
138
173
  end
139
174
  else
140
175
  assert_empty offending_records.map(&:fixture_name),
data/bin/fixture_farm.rb CHANGED
@@ -4,14 +4,24 @@
4
4
  require_relative '../lib/fixture_farm/fixture_recorder'
5
5
 
6
6
  def usage
7
- puts 'Usage: bundle exec fixture_farm <record|status|stop> [fixture_name_prefix]'
7
+ puts 'Usage: bundle exec fixture_farm <record|status|stop> [name_prefix|name_prefix:replaces_name]'
8
8
  exit 1
9
9
  end
10
10
 
11
11
  case ARGV[0]
12
12
  when 'record'
13
- FixtureFarm::FixtureRecorder.start_recording_session!(ARGV[1])
14
- puts "Recording fixtures#{" with prefix #{ARGV[1]}" unless ARGV[1].nil?}"
13
+ prefix_arg = ARGV[1]
14
+
15
+ # Parse hash syntax like "new_user:user_1" into {new_user: :user_1}
16
+ if prefix_arg&.include?(':')
17
+ parts = prefix_arg.split(':', 2)
18
+ parsed_prefix = { parts[0].to_sym => parts[1].to_sym }
19
+ else
20
+ parsed_prefix = prefix_arg
21
+ end
22
+
23
+ FixtureFarm::FixtureRecorder.start_recording_session!(parsed_prefix)
24
+ puts "Recording fixtures#{" with prefix #{prefix_arg}" unless prefix_arg.nil?}"
15
25
  when 'status'
16
26
  if FixtureFarm::FixtureRecorder.recording_session_in_progress?
17
27
  puts 'Recording is on'
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FixtureFarm
4
-
5
4
  mattr_accessor :low_priority_parent_model_for_naming
6
5
 
7
6
  class FixtureRecorder
@@ -16,7 +15,13 @@ module FixtureFarm
16
15
  end
17
16
 
18
17
  def initialize(fixture_name_prefix, new_models = [])
19
- @fixture_name_prefix = fixture_name_prefix
18
+ if fixture_name_prefix.is_a?(Hash)
19
+ @fixture_name_replacements = fixture_name_prefix
20
+ @fixture_name_prefix = nil
21
+ else
22
+ @fixture_name_prefix = fixture_name_prefix
23
+ @fixture_name_replacements = {}
24
+ end
20
25
  @new_models = new_models
21
26
  @deleted_models = {}
22
27
  @initial_now = Time.zone.now
@@ -95,9 +100,11 @@ module FixtureFarm
95
100
  end
96
101
  end
97
102
 
98
- yield self
103
+ result = yield self
99
104
 
100
105
  stop! unless @stopped
106
+
107
+ result
101
108
  ensure
102
109
  ActiveSupport::Notifications.unsubscribe(@subscriber)
103
110
  end
@@ -144,8 +151,10 @@ module FixtureFarm
144
151
 
145
152
  blob.update!(key: new_key)
146
153
 
147
- from_path = Rails.root.join('storage', old_key[0..1], old_key[2..3], old_key)
148
- to_dir = Rails.root.join('storage', new_key[0..1], new_key[2..3])
154
+ blobs_root_path = Pathname.new(ActiveStorage::Blob.service.root)
155
+
156
+ from_path = blobs_root_path.join(old_key[0..1], old_key[2..3], old_key)
157
+ to_dir = blobs_root_path.join(new_key[0..1], new_key[2..3])
149
158
  to_path = to_dir.join(new_key)
150
159
 
151
160
  `mkdir -p #{to_dir}`
@@ -366,12 +375,25 @@ module FixtureFarm
366
375
  fixture_name(model_instance) || begin
367
376
  existing_fixtures = existing_fixtures_for_model(model_instance)
368
377
 
369
- new_fixture_name = [
378
+ base_name = [
370
379
  first_belongs_to_fixture_name(model_instance).presence || @fixture_name_prefix,
371
380
  "#{model_instance.class.name.underscore.split('/').last}_1"
372
381
  ].select(&:present?).join('_')
373
382
 
374
- while @named_new_fixtures[new_fixture_name] || existing_fixtures[new_fixture_name] && !@deleted_models[new_fixture_name]
383
+ @fixture_name_replacements.each do |new_name, old_name|
384
+ # Only apply replacement if the base_name doesn't already start with new_name
385
+ # This prevents double-application of replacements
386
+ next if base_name.start_with?("#{new_name}_")
387
+
388
+ original_name = base_name
389
+ base_name = base_name.gsub(/\b#{Regexp.escape(old_name.to_s)}\b/, new_name.to_s)
390
+
391
+ # If no replacement occurred, use new_name as prefix
392
+ base_name = "#{new_name}_#{base_name}" if base_name == original_name
393
+ end
394
+
395
+ new_fixture_name = base_name
396
+ while @named_new_fixtures[new_fixture_name] || (existing_fixtures[new_fixture_name] && !@deleted_models[new_fixture_name])
375
397
  new_fixture_name = new_fixture_name.sub(/_(\d+)$/, "_#{Regexp.last_match(1).to_i + 1}")
376
398
  end
377
399
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FixtureFarm
4
- VERSION = '1.0.1'
4
+ VERSION = '1.1.1'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fixture_farm
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - artemave
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-07-29 00:00:00.000000000 Z
11
+ date: 2025-08-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails