fixture_farm 1.0.1 → 1.1.2

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: 205e12a455e71ef917424e9451052b3be1517b11d46f9066a5311d51e889a667
4
+ data.tar.gz: 360d8805f8844a6a391cf5877f13a9f2433820a6bb94c0d35ee29444ae08b415
5
5
  SHA512:
6
- metadata.gz: 9c1950bc97570624b27f2f928ec82586816429e1f7a34378f5336c83d8d420180ad65093677744a4b6efa8e3cbc2b7a298631837dbb9ffc32d0c11941d372596
7
- data.tar.gz: 455b1430edd5fb21874e7cd906688ea55ea3e1753c0e02b13b2bcd051f2e68f17d02174f6b770cac9b15f212cef0923b52c3584f40f7b1d662957cff1a4c0cc3
6
+ metadata.gz: 303bb7ecad05b844de71cbe3a68a814ec70a76b4adfa2eaf570752bf6de253c87c85b2a795ec69d6d0b6a7530753a73033df1d2b45366814846d401528272184
7
+ data.tar.gz: 6fdb88f7da8c21e4531152b7a1262b951ed0622a000cd27c61b1fc1f47ccebf5b64ca4c1cad1387e02d93612f82c71c0b73a88e7b0c6f33b2bbcf592a4d33d37
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
  ```
@@ -98,27 +103,63 @@ It's possible to lower the priority of given parent assiciations when it comes t
98
103
  FixtureFarm.low_priority_parent_model_for_naming = -> { _1.is_a?(TenantModel) }
99
104
  ```
100
105
 
106
+ #### Fixture Name Replacement
107
+
108
+ `record_fixtures` also supports hash arguments for advanced fixture naming control:
109
+
110
+ ```ruby
111
+ # Replace 'client_1' with 'new_client' in fixture names, or use 'new_client' as prefix if not found
112
+ record_fixtures(new_client: :client_1) do
113
+ User.create!(name: 'Test User', email: 'test@example.com')
114
+ end
115
+ ```
116
+
117
+ This works in two ways:
118
+ - **Replacement**: If a generated fixture name contains `client_1`, it gets replaced with `new_client`
119
+ - **Prefixing**: If a generated fixture name doesn't contain `client_1`, it gets prefixed with `new_client_`
120
+
121
+ For example:
122
+ - A user fixture that would be named `client_1_user_1` becomes `new_client_user_1` (replacement)
123
+ - A user fixture that would be named `user_1` becomes `new_client_user_1` (prefixing)
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 = ActiveJob::Base.queue_adapter
146
+ # This is so that variants get generated and blobs analyzed
147
+ ActiveJob::Base.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
+ ActiveJob::Base.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}`
@@ -257,6 +266,8 @@ module FixtureFarm
257
266
  value.to_f
258
267
  when Hash
259
268
  value.to_json
269
+ when IPAddr
270
+ value.to_s
260
271
  else
261
272
  value
262
273
  end
@@ -366,12 +377,25 @@ module FixtureFarm
366
377
  fixture_name(model_instance) || begin
367
378
  existing_fixtures = existing_fixtures_for_model(model_instance)
368
379
 
369
- new_fixture_name = [
380
+ base_name = [
370
381
  first_belongs_to_fixture_name(model_instance).presence || @fixture_name_prefix,
371
382
  "#{model_instance.class.name.underscore.split('/').last}_1"
372
383
  ].select(&:present?).join('_')
373
384
 
374
- while @named_new_fixtures[new_fixture_name] || existing_fixtures[new_fixture_name] && !@deleted_models[new_fixture_name]
385
+ @fixture_name_replacements.each do |new_name, old_name|
386
+ # Only apply replacement if the base_name doesn't already start with new_name
387
+ # This prevents double-application of replacements
388
+ next if base_name.start_with?("#{new_name}_")
389
+
390
+ original_name = base_name
391
+ base_name = base_name.gsub(/\b#{Regexp.escape(old_name.to_s)}\b/, new_name.to_s)
392
+
393
+ # If no replacement occurred, use new_name as prefix
394
+ base_name = "#{new_name}_#{base_name}" if base_name == original_name
395
+ end
396
+
397
+ new_fixture_name = base_name
398
+ while @named_new_fixtures[new_fixture_name] || (existing_fixtures[new_fixture_name] && !@deleted_models[new_fixture_name])
375
399
  new_fixture_name = new_fixture_name.sub(/_(\d+)$/, "_#{Regexp.last_match(1).to_i + 1}")
376
400
  end
377
401
 
@@ -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.2'
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.2
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-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails