active_capture 1.1.0 → 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: '03486bb96c7c947866d02dbd2a0d3427c81b8ccaea70440ce5ff4463a1905c43'
4
- data.tar.gz: ba44f0ab8b7d31f34a9c2470031569514bc5346ad1acbd328a74d2f06ddb7b27
3
+ metadata.gz: 35e9f57cd205fbbb0ed7e4139101787cb4f66e53a32186b27473f9db72b40498
4
+ data.tar.gz: ea2747f578ebc4dff315d542b7e6e4cb9193e53e0dd49f2231f37654d39047bc
5
5
  SHA512:
6
- metadata.gz: e116aa0888362fd7f400bff904af9051a9aaaf60c941bd609008db77940c1007e5f20369ac5490cf918355cb3cfe693677074be0b98500e7346fd60f54fcccee
7
- data.tar.gz: d3857c7bea275437fe8af8df3337c33da86bd2dfcc8738faea767c4081934381bfc14d3911c34a500f02d5bee0f219858f493af79b259e27fa2030e190578e6d
6
+ metadata.gz: f72ad16293a0f39eed9e2471ba7ece51022abdcb6f6005244f25502f7d903eb5054b983a2414fbbeb18c0b1878a93bf674333c71bcd4e4cea57a37df841cc8bf
7
+ data.tar.gz: c4d02f5b01eb45a1a2e98cd349b4fd3d765a8a4ff27dfe48a57568ac06594431ba501dd69652292776724f548c8528f92b3cd1a613cff5c0a5f22024d4355868
@@ -1,106 +1,143 @@
1
- require 'active_capture/version'
2
- require_relative 'active_capture/capture_storage'
1
+ require 'support/version'
2
+ require_relative 'support/capture_storage'
3
3
  require 'json'
4
4
  require 'active_record'
5
5
 
6
6
  module ActiveCapture
7
- class Capture
8
- def self.take(record, associations: [], name: nil)
9
- raise ArgumentError, 'Record must be an ActiveRecord::Base instance' unless record.is_a?(ActiveRecord::Base)
10
-
11
- capture_data = {
12
- model: record.class.name,
13
- record_id: record.id,
14
- attributes: record.attributes,
15
- associations: capture_associations(record, associations)
16
- }
17
-
18
- CaptureStorage.save_capture(record, capture_data, name)
7
+ def self.take(record, associations: [], name: nil)
8
+ validate_record(record)
9
+
10
+ capture_data = {
11
+ model: record.class.name,
12
+ record_id: record.id,
13
+ attributes: record.attributes,
14
+ associations: capture_associations(record, associations)
15
+ }
16
+
17
+ begin
18
+ Support::CaptureStorage.save_capture(record, capture_data, name)
19
+ rescue StandardError => e
20
+ raise "Failed to save capture: #{e.message}"
19
21
  end
22
+ end
20
23
 
21
- def self.restore(record, capture_file, merge: false)
22
- captured_records = CaptureStorage.load_capture(capture_file)
23
-
24
- raise ArgumentError, 'capture file does not match the given record' unless captured_records['record_id'] == record.id
24
+ def self.restore(record, capture_file, merge: false)
25
+ captured_records = Support::CaptureStorage.load_capture(capture_file)
26
+ validate_capture(record, captured_records)
25
27
 
26
- ActiveRecord::Base.transaction do
28
+ ActiveRecord::Base.transaction do
29
+ begin
27
30
  record.update!(captured_records['attributes'])
28
31
  restore_associations(record, captured_records['associations'], merge: merge)
32
+ rescue StandardError => e
33
+ raise "Failed to restore record: #{e.message}"
29
34
  end
30
35
  end
36
+ end
31
37
 
32
- private
38
+ private
33
39
 
34
- def self.capture_associations(record, associations)
35
- association_data = {}
40
+ def self.validate_record(record)
41
+ unless record.is_a?(ActiveRecord::Base)
42
+ raise ArgumentError, 'Record must be an ActiveRecord::Base instance'
43
+ end
44
+ end
45
+
46
+ def self.validate_capture(record, captured_records)
47
+ unless captured_records['record_id'] == record.id
48
+ raise ArgumentError, 'Capture file does not match the given record'
49
+ end
50
+ end
36
51
 
37
- associations.each do |association|
38
- nested_associations = []
52
+ def self.capture_associations(record, associations)
53
+ associations.each_with_object({}) do |association, association_data|
54
+ association_name, nested_associations = parse_association(association)
39
55
 
40
- if association.is_a?(Hash)
41
- association_name = association.keys.first
42
- nested_associations = association.values.flatten
43
- else
44
- association_name = association
45
- end
56
+ next unless record.respond_to?(association_name)
46
57
 
47
- if record.respond_to?(association_name)
48
- related_records = record.send(association_name)
49
-
50
- if related_records.is_a?(ActiveRecord::Base)
51
- association_data[association_name] = {
52
- attributes: related_records.attributes,
53
- associations: capture_associations(related_records, nested_associations)
54
- }
55
- elsif related_records.respond_to?(:map)
56
- association_data[association_name] = related_records.map do |related_record|
57
- {
58
- attributes: related_record.attributes,
59
- associations: capture_associations(related_record, nested_associations)
60
- }
61
- end
62
- end
63
- end
58
+ begin
59
+ related_records = record.send(association_name)
60
+ association_data[association_name] = capture_related_records(related_records, nested_associations)
61
+ rescue StandardError => e
62
+ raise "Failed to capture association #{association_name}: #{e.message}"
64
63
  end
64
+ end
65
+ end
65
66
 
66
- association_data
67
+ def self.capture_related_records(related_records, nested_associations)
68
+ if related_records.is_a?(ActiveRecord::Base)
69
+ {
70
+ attributes: related_records.attributes,
71
+ associations: capture_associations(related_records, nested_associations)
72
+ }
73
+ elsif related_records.respond_to?(:map)
74
+ related_records.map do |related_record|
75
+ {
76
+ attributes: related_record.attributes,
77
+ associations: capture_associations(related_record, nested_associations)
78
+ }
79
+ end
67
80
  end
81
+ end
68
82
 
69
- def self.restore_associations(record, associations_data, merge: false)
70
- associations_data.each do |association_name, related_data|
71
- if related_data.is_a?(Array)
72
- associated_class = record.class.reflect_on_association(association_name).klass
73
- existing_records = record.send(association_name)
74
-
75
- related_data.each do |related_record_data|
76
- if related_record_data['attributes']['id'] && existing_record = existing_records.find_by(id: related_record_data['attributes']['id'])
77
- existing_record.update!(related_record_data['attributes'])
78
- restore_associations(existing_record, related_record_data['associations'], merge: merge)
79
- else
80
- new_record = associated_class.create!(related_record_data['attributes'])
81
- restore_associations(new_record, related_record_data['associations'], merge: merge)
82
- end
83
- end
84
- else
85
- associated_class = record.class.reflect_on_association(association_name).klass
86
- if merge && related_data['attributes']['id']
87
- existing_record = record.send(association_name)
88
-
89
- if existing_record && existing_record.id == related_data['attributes']['id']
90
- existing_record.update!(related_data['attributes'])
91
- restore_associations(existing_record, related_data['associations'], merge: merge)
92
- else
93
- new_record = associated_class.create!(related_data['attributes'])
94
- record.update!(association_name => new_record)
95
- restore_associations(new_record, related_data['associations'], merge: merge)
96
- end
97
- else
98
- new_record = associated_class.create!(related_data['attributes'])
99
- record.update!(association_name => new_record)
100
- restore_associations(new_record, related_data['associations'], merge: merge)
101
- end
83
+ def self.parse_association(association)
84
+ if association.is_a?(Hash)
85
+ [association.keys.first, association.values.flatten]
86
+ else
87
+ [association, []]
88
+ end
89
+ end
90
+
91
+ def self.restore_associations(record, associations_data, merge: false)
92
+ associations_data.each do |association_name, related_data|
93
+ if related_data.is_a?(Array)
94
+ restore_collection_association(record, association_name, related_data, merge)
95
+ else
96
+ restore_single_association(record, association_name, related_data, merge)
97
+ end
98
+ end
99
+ end
100
+
101
+ def self.restore_collection_association(record, association_name, related_data, merge)
102
+ associated_class = record.class.reflect_on_association(association_name).klass
103
+ existing_records = record.send(association_name)
104
+
105
+ related_data.each do |related_record_data|
106
+ if merge && related_record_data['attributes']['id']
107
+ existing_record = existing_records.find_by(id: related_record_data['attributes']['id'])
108
+ if existing_record
109
+ update_and_restore(existing_record, related_record_data, merge)
110
+ next
102
111
  end
103
112
  end
113
+ create_and_restore(associated_class, related_record_data, record, association_name)
104
114
  end
105
115
  end
116
+
117
+ def self.restore_single_association(record, association_name, related_data, merge)
118
+ associated_class = record.class.reflect_on_association(association_name).klass
119
+
120
+ if merge && related_data['attributes']['id']
121
+ existing_record = record.send(association_name)
122
+ if existing_record&.id == related_data['attributes']['id']
123
+ update_and_restore(existing_record, related_data, merge)
124
+ return
125
+ end
126
+ end
127
+
128
+ new_record = create_and_restore(associated_class, related_data)
129
+ record.update!(association_name => new_record)
130
+ end
131
+
132
+ def self.update_and_restore(record, related_data, merge)
133
+ record.update!(related_data['attributes'])
134
+ restore_associations(record, related_data['associations'], merge: merge)
135
+ end
136
+
137
+ def self.create_and_restore(associated_class, related_data, parent_record = nil, association_name = nil)
138
+ new_record = associated_class.create!(related_data['attributes'])
139
+ restore_associations(new_record, related_data['associations'], merge: false)
140
+ parent_record&.update!(association_name => new_record) if association_name
141
+ new_record
142
+ end
106
143
  end
@@ -0,0 +1,151 @@
1
+ require 'json'
2
+ require 'fileutils'
3
+ require 'pathname'
4
+
5
+ module Support
6
+ class CaptureStorage
7
+ CAPTURE_DIR = 'captures'.freeze
8
+ MAX_FILENAME_LENGTH = 100
9
+ VALID_FILENAME_REGEX = /\A[a-zA-Z0-9\-_\.]+\z/
10
+
11
+ class << self
12
+ def save_capture(record, capture_data, name = nil)
13
+ validate_record(record)
14
+ validate_capture_data(capture_data)
15
+
16
+ file_name = generate_filename(record, name)
17
+ dir_path = create_capture_directory(record)
18
+ file_path = File.join(dir_path, file_name)
19
+
20
+ write_capture_file(file_path, capture_data)
21
+
22
+ { success: true, file_path: file_path }
23
+ rescue => e
24
+ { success: false, error: e.message }
25
+ end
26
+
27
+ def load_capture(capture_file)
28
+ validate_file_path(capture_file)
29
+
30
+ file_content = read_file_safely(capture_file)
31
+ parsed_data = JSON.parse(file_content)
32
+
33
+ validate_parsed_data(parsed_data)
34
+
35
+ parsed_data
36
+ rescue => e
37
+ raise StorageError, "Failed to load capture: #{e.message}"
38
+ end
39
+
40
+ private
41
+
42
+ def validate_record(record)
43
+ unless record.respond_to?(:class) && record.respond_to?(:id)
44
+ raise ArgumentError, "Invalid record: must respond to :class and :id"
45
+ end
46
+
47
+ if record.id.nil?
48
+ raise ArgumentError, "Cannot capture unsaved record (ID is nil)"
49
+ end
50
+ end
51
+
52
+ def validate_capture_data(data)
53
+ unless data.is_a?(Hash)
54
+ raise ArgumentError, "Capture data must be a Hash"
55
+ end
56
+
57
+ required_keys = [:model, :record_id, :attributes]
58
+ missing_keys = required_keys.reject { |k| data.key?(k) }
59
+ unless missing_keys.empty?
60
+ raise ArgumentError, "Missing required keys in capture data: #{missing_keys.join(', ')}"
61
+ end
62
+ end
63
+
64
+ def generate_filename(record, custom_name)
65
+ model_name = record.class.name.downcase.gsub(/[^a-z0-9]/, '_')
66
+ record_id = record.id.to_s
67
+ timestamp = Time.now.strftime('%Y%m%d%H%M%S')
68
+
69
+ if custom_name
70
+ validate_filename(custom_name)
71
+ base_name = custom_name.gsub(/\s+/, '_')[0..MAX_FILENAME_LENGTH]
72
+ "#{base_name}.json"
73
+ else
74
+ "#{model_name}_#{record_id}_#{timestamp}.json"
75
+ end
76
+ end
77
+
78
+ def validate_filename(name)
79
+ if name.empty?
80
+ raise ArgumentError, "Filename cannot be empty"
81
+ end
82
+
83
+ if name.length > MAX_FILENAME_LENGTH
84
+ raise ArgumentError, "Filename too long (max #{MAX_FILENAME_LENGTH} chars)"
85
+ end
86
+
87
+ unless name.match(VALID_FILENAME_REGEX)
88
+ raise ArgumentError, "Filename contains invalid characters"
89
+ end
90
+ end
91
+
92
+ def create_capture_directory(record)
93
+ model_name = record.class.name.downcase.gsub(/[^a-z0-9]/, '_')
94
+ dir_path = File.join(CAPTURE_DIR, model_name)
95
+
96
+ begin
97
+ FileUtils.mkdir_p(dir_path)
98
+ rescue SystemCallError => e
99
+ raise StorageError, "Failed to create directory '#{dir_path}': #{e.message}"
100
+ end
101
+
102
+ dir_path
103
+ end
104
+
105
+ def write_capture_file(file_path, data)
106
+ begin
107
+ temp_path = "#{file_path}.tmp"
108
+ File.write(temp_path, JSON.pretty_generate(data))
109
+ File.rename(temp_path, file_path)
110
+ rescue SystemCallError => e
111
+ File.delete(temp_path) if File.exist?(temp_path)
112
+ raise StorageError, "Failed to write capture file '#{file_path}': #{e.message}"
113
+ end
114
+ end
115
+
116
+ def validate_file_path(file_path)
117
+ unless File.exist?(file_path)
118
+ raise StorageError, "File does not exist: #{file_path}"
119
+ end
120
+
121
+ unless File.readable?(file_path)
122
+ raise StorageError, "No read permission for file: #{file_path}"
123
+ end
124
+
125
+ if File.directory?(file_path)
126
+ raise StorageError, "Path is a directory, not a file: #{file_path}"
127
+ end
128
+ end
129
+
130
+ def read_file_safely(file_path)
131
+ File.read(file_path)
132
+ rescue SystemCallError => e
133
+ raise StorageError, "Failed to read file '#{file_path}': #{e.message}"
134
+ end
135
+
136
+ def validate_parsed_data(data)
137
+ unless data.is_a?(Hash)
138
+ raise StorageError, "Invalid capture format: expected JSON object"
139
+ end
140
+
141
+ required_keys = ['model', 'record_id', 'attributes']
142
+ missing_keys = required_keys.reject { |k| data.key?(k) }
143
+ unless missing_keys.empty?
144
+ raise StorageError, "Invalid capture data: missing keys #{missing_keys.join(', ')}"
145
+ end
146
+ end
147
+ end
148
+
149
+ class StorageError < StandardError; end
150
+ end
151
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Support
4
+ VERSION = "1.1.1"
5
+ end
@@ -0,0 +1,155 @@
1
+ require 'test_helper'
2
+ require 'minitest/autorun'
3
+ require 'fileutils'
4
+
5
+ # Define our test classes at the top level
6
+ class TestModel; end
7
+ class NewModel; end
8
+ class ProtectedModel; end
9
+
10
+ class CaptureStorageTest < Minitest::Test
11
+ # Simple test record class that mimics ActiveRecord::Base
12
+ class TestRecord
13
+ attr_reader :id
14
+
15
+ def initialize(id, class_name)
16
+ @id = id
17
+ @class_name = class_name
18
+ end
19
+
20
+ def class
21
+ @class_name.constantize
22
+ end
23
+ end
24
+
25
+ def setup
26
+ # Create test data directory
27
+ @test_dir = 'test_data'
28
+ FileUtils.mkdir_p(@test_dir)
29
+
30
+ # Create sample capture data
31
+ @sample_data = {
32
+ model: 'TestModel',
33
+ record_id: 1,
34
+ attributes: { name: 'Test', value: 42 },
35
+ associations: {
36
+ comments: [
37
+ { attributes: { id: 1, content: 'First comment' } },
38
+ { attributes: { id: 2, content: 'Second comment' } }
39
+ ]
40
+ }
41
+ }
42
+
43
+ # Create test files
44
+ @valid_file = "#{@test_dir}/valid.json"
45
+ File.write(@valid_file, JSON.generate(@sample_data))
46
+
47
+ @invalid_json_file = "#{@test_dir}/invalid.json"
48
+ File.write(@invalid_json_file, 'not valid json')
49
+
50
+ @empty_file = "#{@test_dir}/empty.json"
51
+ File.write(@empty_file, '')
52
+
53
+ # Clear captures directory before each test
54
+ FileUtils.rm_rf(Support::CaptureStorage::CAPTURE_DIR) if File.directory?(Support::CaptureStorage::CAPTURE_DIR)
55
+ end
56
+
57
+ def teardown
58
+ # Clean up test files
59
+ FileUtils.rm_rf(@test_dir)
60
+ FileUtils.rm_rf(Support::CaptureStorage::CAPTURE_DIR)
61
+ end
62
+
63
+ def test_save_capture_creates_valid_file
64
+ test_record = TestRecord.new(1, 'TestModel')
65
+ result = Support::CaptureStorage.save_capture(test_record, @sample_data)
66
+
67
+ assert result[:success], "Save should succeed: #{result[:error]}"
68
+ assert File.exist?(result[:file_path]), "File should exist at #{result[:file_path]}"
69
+
70
+ file_content = JSON.parse(File.read(result[:file_path]))
71
+ assert_equal 'TestModel', file_content['model']
72
+ assert_equal 1, file_content['record_id']
73
+ end
74
+
75
+ def test_save_capture_with_custom_name
76
+ test_record = TestRecord.new(1, 'TestModel')
77
+ custom_name = 'custom_capture'
78
+ result = Support::CaptureStorage.save_capture(test_record, @sample_data, custom_name)
79
+
80
+ assert result[:success], "Save should succeed: #{result[:error]}"
81
+ assert_match /#{custom_name}\.json/, result[:file_path]
82
+ assert File.exist?(result[:file_path]), "File should exist at #{result[:file_path]}"
83
+ end
84
+
85
+ def test_save_capture_invalid_filename
86
+ test_record = TestRecord.new(1, 'TestModel')
87
+ result = Support::CaptureStorage.save_capture(test_record, @sample_data, 'invalid/name')
88
+
89
+ refute result[:success], "Save should fail with invalid filename"
90
+ assert_match /Filename contains invalid characters/, result[:error], "Error should mention invalid filename"
91
+ end
92
+
93
+ def test_load_capture_valid_file
94
+ data = Support::CaptureStorage.load_capture(@valid_file)
95
+
96
+ assert_equal 'TestModel', data['model']
97
+ assert_equal 1, data['record_id']
98
+ assert_equal 2, data['associations']['comments'].size
99
+ end
100
+
101
+ def test_load_capture_nonexistent_file
102
+ assert_raises Support::CaptureStorage::StorageError do
103
+ Support::CaptureStorage.load_capture('nonexistent.json')
104
+ end
105
+ end
106
+
107
+ def test_load_capture_invalid_json
108
+ assert_raises Support::CaptureStorage::StorageError do
109
+ Support::CaptureStorage.load_capture(@invalid_json_file)
110
+ end
111
+ end
112
+
113
+ def test_load_capture_empty_file
114
+ assert_raises Support::CaptureStorage::StorageError do
115
+ Support::CaptureStorage.load_capture(@empty_file)
116
+ end
117
+ end
118
+
119
+ def test_load_capture_missing_required_keys
120
+ incomplete_data = { 'some_key' => 'some_value' }
121
+ incomplete_file = "#{@test_dir}/incomplete.json"
122
+ File.write(incomplete_file, JSON.generate(incomplete_data))
123
+
124
+ assert_raises Support::CaptureStorage::StorageError do
125
+ Support::CaptureStorage.load_capture(incomplete_file)
126
+ end
127
+ end
128
+
129
+ def test_atomic_write_handles_failure
130
+ # Create a directory we can't write to
131
+ protected_dir = "#{@test_dir}/protected"
132
+ FileUtils.mkdir_p(protected_dir)
133
+ FileUtils.chmod(0444, protected_dir) # read-only
134
+
135
+ test_record = TestRecord.new(1, 'ProtectedModel')
136
+ file_path = "#{protected_dir}/test.json"
137
+
138
+ assert_raises Support::CaptureStorage::StorageError do
139
+ Support::CaptureStorage.send(:write_capture_file, file_path, @sample_data)
140
+ end
141
+
142
+ # Verify no temp file remains
143
+ refute File.exist?("#{file_path}.tmp")
144
+ ensure
145
+ FileUtils.chmod(0755, protected_dir) if File.exist?(protected_dir)
146
+ end
147
+
148
+ def test_directory_creation
149
+ test_record = TestRecord.new(1, 'NewModel')
150
+ result = Support::CaptureStorage.save_capture(test_record, @sample_data)
151
+
152
+ assert result[:success], "Save should succeed: #{result[:error]}"
153
+ assert File.directory?('captures/newmodel'), "Directory should be created"
154
+ end
155
+ end
@@ -2,6 +2,7 @@ require 'test_helper'
2
2
  require 'minitest/autorun'
3
3
 
4
4
  class ActiveCaptureTest < Minitest::Test
5
+ # Setup method to initialize test data before each test
5
6
  def setup
6
7
  @user = User.create!(name: "John Doe", email: "john@example.com")
7
8
  @post = @user.posts.create!(title: "First Post", content: "This is my first post.")
@@ -9,29 +10,50 @@ class ActiveCaptureTest < Minitest::Test
9
10
  @comment2 = @post.comments.create!(content: "Thanks for sharing.")
10
11
  end
11
12
 
13
+ # Teardown method to clean up after each test
14
+ def teardown
15
+ FileUtils.rm_rf('captures/')
16
+ end
17
+
18
+ # Test capturing a user with associated posts and comments
12
19
  def test_capture_with_associations
13
- ActiveCapture::Capture.take(@user, associations: [:posts, { posts: :comments }])
20
+ # Take a capture of the user and their associations
21
+ ActiveCapture.take(@user, associations: [:posts, { posts: :comments }])
14
22
 
23
+ # Verify that capture files are created
15
24
  capture_files = Dir["captures/user/*.json"]
16
25
  assert !capture_files.empty?, "capture file should be created"
17
26
 
27
+ # Verify the content of the capture file
18
28
  capture_content = JSON.parse(File.read(capture_files.last))
19
29
  assert_equal 1, capture_content["associations"]["posts"].size
20
30
  assert_equal 2, capture_content["associations"]["posts"].first["associations"]["comments"].size
21
31
  end
22
32
 
33
+ # Test restoring a user with merge functionality
23
34
  def test_restore_with_merge
24
- ActiveCapture::Capture.take(@user, associations: [:posts, { posts: :comments }])
35
+ # Take a capture of the user and their associations
36
+ ActiveCapture.take(@user, associations: [:posts, { posts: :comments }])
25
37
  capture_file = Dir["captures/user/*.json"].last
38
+
39
+ # Modify the post and add a new comment
26
40
  @post.update(title: "updated title")
27
41
  new_comment = @post.comments.create!(content: "Another comment")
28
- ActiveCapture::Capture.restore(@user, capture_file, merge: true)
29
42
 
43
+ # Restore the user from the capture file with merge enabled
44
+ ActiveCapture.restore(@user, capture_file, merge: true)
45
+
46
+ # Reload the user and post to verify changes
30
47
  @user.reload
31
48
  @post.reload
32
49
 
50
+ # Verify that the user's name is restored
33
51
  assert_equal "John Doe", @user.name
52
+
53
+ # Verify that the post's title is restored
34
54
  assert_equal "First Post", @post.title
55
+
56
+ # Verify that the new comment still exists after the merge
35
57
  assert_equal 3, @post.comments.count
36
58
  assert @post.comments.exists?(new_comment.id), "New comment should still exist after merge"
37
59
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_capture
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tanmay Bhawsar
@@ -32,8 +32,9 @@ extensions: []
32
32
  extra_rdoc_files: []
33
33
  files:
34
34
  - lib/active_capture.rb
35
- - lib/active_capture/capture_storage.rb
36
- - lib/active_capture/version.rb
35
+ - lib/support/capture_storage.rb
36
+ - lib/support/version.rb
37
+ - test/support/test_capture_storage.rb
37
38
  - test/test_active_capture.rb
38
39
  - test/test_helper.rb
39
40
  homepage: https://github.com/bhawsartanmay/active_capture
@@ -1,29 +0,0 @@
1
- require 'json'
2
- require 'fileutils'
3
-
4
- module ActiveCapture
5
- class CaptureStorage
6
- CAPTURE_DIR = 'captures'
7
-
8
- def self.save_capture(record, capture_data, name = nil)
9
- model_name = record.class.name.downcase
10
- record_id = record.id
11
- timestamp = Time.now.strftime('%Y%m%d%H%M%S')
12
- file_name = name ? "#{name}.json" : "#{record_id}_#{timestamp}.json"
13
-
14
- dir_path = File.join(CAPTURE_DIR, model_name)
15
- FileUtils.mkdir_p(dir_path)
16
-
17
- file_path = File.join(dir_path, file_name)
18
- File.write(file_path, JSON.pretty_generate(capture_data))
19
-
20
- puts "Capture saved to #{file_path}"
21
- end
22
-
23
- def self.load_capture(capture_file)
24
- raise ArgumentError, "Capture file does not exist #{capture_file}" unless File.exist?(capture_file)
25
-
26
- JSON.parse(File.read(capture_file))
27
- end
28
- end
29
- end
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActiveCapture
4
- VERSION = "1.1.0"
5
- end