fixture_record 0.1.1.pre.rc → 0.1.2.pre.rc
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 +4 -4
- data/README.md +80 -0
- data/lib/fixture_record/association_traversal.rb +20 -1
- data/lib/fixture_record/configuration.rb +14 -0
- data/lib/fixture_record/naming.rb +8 -1
- data/lib/fixture_record/sanitizable.rb +16 -16
- data/lib/fixture_record/sanitizers/simple_timestamp.rb +1 -2
- data/lib/fixture_record/version.rb +1 -1
- data/lib/fixture_record.rb +16 -1
- data/lib/generators/fixture_record/install/install_generator.rb +11 -5
- data/lib/generators/fixture_record/install/templates/initializer.rb +14 -7
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e0b73f48614260bc6e1dc1f0ecebcabe4b5bb44779eb3ba807be3942fd8439fc
|
4
|
+
data.tar.gz: 2af43ba19fd5cde723d3dede76fd21eecd17f92d1ad38693702d14fbd85939fa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 630ec0346571b9f9a35f2a1f319106e4041372a97e4da36980ac24038b2bc8a6f19fc64d6274813834a46a817d148cfdb3961cb9be5a9ede11a20dc7ba543b23
|
7
|
+
data.tar.gz: 1c5fa27c4b2bd560bee90fb1461d53a55fc64e380c7df15876b080b17eea315b7dc3290536b3217c758a6929477c2194766e7bb09d363607414f490c3f766d49
|
data/README.md
CHANGED
@@ -124,6 +124,86 @@ foo_post_49:
|
|
124
124
|
...
|
125
125
|
```
|
126
126
|
|
127
|
+
## Data Sanitizing and Obfuscation
|
128
|
+
`FixtureRecord` supports mutating data before writing the data. By default, `FixtureRecord` has a built in sanitizer that is used for `created_at` and `updated_at` fields. The reason for the `simple_timestamp` is that Rails will turn a timestamp into a complex object when calling `to_yaml`.
|
129
|
+
```ruby
|
130
|
+
user.attributes.to_yaml
|
131
|
+
# =>
|
132
|
+
id: ...
|
133
|
+
created_at: !ruby/object:ActiveSupport::TimeWithZone
|
134
|
+
utc: 2024-03-17 23:11:31.329037000 Z
|
135
|
+
zone: &1 !ruby/object:ActiveSupport::TimeZone
|
136
|
+
name: Etc/UTC
|
137
|
+
time: 2024-03-17 23:11:31.329037000 Z
|
138
|
+
updated_at: !ruby/object:ActiveSupport::TimeWithZone
|
139
|
+
utc: 2024-03-17 23:11:31.329037000 Z
|
140
|
+
zone: *1
|
141
|
+
time: 2024-03-17 23:11:31.329037000 Z
|
142
|
+
```
|
143
|
+
This type of timestamp structure isn't needed and can simply clutter up a fixture file. So instead, `FixtureRecord` comes with a sanitizer to clean this up.
|
144
|
+
```ruby
|
145
|
+
# lib/fixture_record/sanitizers/simple_timestamp
|
146
|
+
module FixtureRecord
|
147
|
+
module Sanitizers
|
148
|
+
class SimpleTimestamp < FixtureRecord::Sanitizer
|
149
|
+
def cast(value)
|
150
|
+
value.to_s
|
151
|
+
end
|
152
|
+
end
|
153
|
+
FixtureRecord.registry.register_sanitizer(FixtureRecord::Sanitizers::SimpleTimestamp, as: :simple_timestamp)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# fixture_record/initializer.rb (created using the install script)
|
158
|
+
FixtureRecord.configure do |config|
|
159
|
+
...
|
160
|
+
|
161
|
+
config.sanitize_pattern /created_at$|updated_at$/, with: :simple_timestamp
|
162
|
+
|
163
|
+
...
|
164
|
+
end
|
165
|
+
```
|
166
|
+
|
167
|
+
In this case, any column the regex pattern of 'created_at' or 'updated_at' will have its value passed to the registered sanitizer class.
|
168
|
+
|
169
|
+
### Creating a Custom Sanitizer
|
170
|
+
Step one, create the custom class. Inheriting from `FixtureRecord::Sanitizer` is not a requirement currently, but there might be some nice-to-have features as part of that class in the future. At minimum, your custom class needs to have at minimum a `#cast` instance method that will receive the value that is to be sanitized and returns the newly converted value. Currently, `#cast` will be called whether the value is `nil` or not, so be sure your method can handle the `nil` scenario.
|
171
|
+
```ruby
|
172
|
+
class MyReverseSanitizer < FixtureRecord::Sanitizer
|
173
|
+
def cast(value)
|
174
|
+
value&.reverse
|
175
|
+
end
|
176
|
+
end
|
177
|
+
```
|
178
|
+
|
179
|
+
### Registering the Sanitizer
|
180
|
+
In your custom class, or in the initializer, register the new sanitizer.
|
181
|
+
```ruby
|
182
|
+
class MyReverseSanitizer < FixtureRecord::Sanitizer
|
183
|
+
...
|
184
|
+
end
|
185
|
+
|
186
|
+
FixtureRecord.registry.register_sanitizer MyReverseSanitizer, :reverse
|
187
|
+
```
|
188
|
+
### Assiging the Sanitizer to a Pattern
|
189
|
+
In the fixture record initializer, use `#sanitize_pattern` to assign the registered sanitizer to a regex pattern. In the following example code, any column that matches `email` would be sent through the reverse sanitizer, this would include `email`, `user_email`, `primary_email`, etc.
|
190
|
+
```ruby
|
191
|
+
# fixture_record/initializer.rb
|
192
|
+
FixtureRecord.configure do |config|
|
193
|
+
...
|
194
|
+
|
195
|
+
config.sanitize_pattern /email/, with: :reverse
|
196
|
+
|
197
|
+
...
|
198
|
+
end
|
199
|
+
```
|
200
|
+
|
201
|
+
The pattern that is used for comparison is inclusive of the class name as well. So if you need a sanitizer to be scoped to a specific class you can use the class name in the regex pattern. Taking the example above:
|
202
|
+
```ruby
|
203
|
+
config.sanitize_pattern /User.email/, with: :reverse
|
204
|
+
```
|
205
|
+
Now columns on other classes that include `email` in their name won't be passed to the sanitizer. Also keep in mind the mechanism being used here is basic regex pattern matching, so `User.primary_email` wouldn't match in this case and would not be sent to the sanitizer.
|
206
|
+
|
127
207
|
## Installation
|
128
208
|
`FixtureRecord` is only needed as a development dependency.
|
129
209
|
```ruby
|
@@ -2,7 +2,10 @@ module FixtureRecord
|
|
2
2
|
module AssociationTraversal
|
3
3
|
class UnrecognizedAssociationError < StandardError; end
|
4
4
|
|
5
|
+
attr_accessor :_traversed_fixture_record_associations
|
6
|
+
|
5
7
|
def traverse_fixture_record_associations(*associations)
|
8
|
+
self._traversed_fixture_record_associations = []
|
6
9
|
associations.each do |association|
|
7
10
|
Builder.new(self, association).build
|
8
11
|
end
|
@@ -36,8 +39,13 @@ module FixtureRecord
|
|
36
39
|
def build
|
37
40
|
raise UnrecognizedAssociationError.new(
|
38
41
|
"#{@symbol} is not a recognized association or method on #{@source_record.class}. Is it misspelled?"
|
39
|
-
) unless
|
42
|
+
) unless klass_association.present?
|
43
|
+
|
44
|
+
if through_assoc_option && @source_record._traversed_fixture_record_associations.exclude?(through_assoc_option)
|
45
|
+
infill_through
|
46
|
+
end
|
40
47
|
|
48
|
+
@source_record._traversed_fixture_record_associations << @symbol
|
41
49
|
built_records = Array.wrap(@source_record.send(@symbol)).compact_blank
|
42
50
|
return unless built_records.present?
|
43
51
|
|
@@ -48,6 +56,17 @@ module FixtureRecord
|
|
48
56
|
end
|
49
57
|
end
|
50
58
|
|
59
|
+
def infill_through
|
60
|
+
SymbolBuilder.new(@source_record, through_assoc_option).build
|
61
|
+
end
|
62
|
+
|
63
|
+
def through_assoc_option
|
64
|
+
klass_association.options[:through]
|
65
|
+
end
|
66
|
+
|
67
|
+
def klass_association
|
68
|
+
@source_record.class.reflect_on_association(@symbol)
|
69
|
+
end
|
51
70
|
end
|
52
71
|
|
53
72
|
class HashBuilder
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module FixtureRecord
|
2
|
+
class Configuration
|
3
|
+
def name_records_with(proc_or_klass)
|
4
|
+
FixtureRecord.naming = proc_or_klass.is_a?(Class) ? proc_or_klass.new : proc_or_klass
|
5
|
+
end
|
6
|
+
|
7
|
+
def sanitize_pattern(pattern, with:)
|
8
|
+
registry_name_or_klass = with
|
9
|
+
klass = registry_name_or_klass.is_a?(Symbol) ? FixtureRecord.registry[registry_name_or_klass] : registry_name_or_klass
|
10
|
+
klass_instance = klass.is_a?(Class) ? klass.new : klass
|
11
|
+
FixtureRecord.registry.sanitize_pattern pattern, with: klass_instance
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -3,8 +3,15 @@ module FixtureRecord
|
|
3
3
|
attr_accessor :fixture_record_prefix
|
4
4
|
attr_accessor :fixture_record_suffix
|
5
5
|
|
6
|
+
class Base
|
7
|
+
def call(record)
|
8
|
+
|
9
|
+
[record.fixture_record_prefix, record.class.model_name.param_key, record.id || 'new', record.fixture_record_suffix].compact_blank.join('_')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
6
13
|
def test_fixture_name
|
7
|
-
|
14
|
+
FixtureRecord.naming.call(self)
|
8
15
|
end
|
9
16
|
end
|
10
17
|
end
|
@@ -1,9 +1,6 @@
|
|
1
1
|
module FixtureRecord::Sanitizable
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
|
-
class_methods do
|
5
|
-
end
|
6
|
-
|
7
4
|
def sanitize_attributes_for_test_fixture
|
8
5
|
_fixture_record_attributes.each do |attr, value|
|
9
6
|
registry_key = [self.class.name, attr.to_s].join('.')
|
@@ -24,24 +21,27 @@ module FixtureRecord::Sanitizable
|
|
24
21
|
end
|
25
22
|
|
26
23
|
class Registry
|
27
|
-
@_fixture_record_sanitizer_pattern_registry =
|
24
|
+
@_fixture_record_sanitizer_pattern_registry = []
|
28
25
|
@_fixture_record_sanitizer_name_registry = {}
|
29
26
|
|
30
|
-
def self.
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
27
|
+
def self.[](...)
|
28
|
+
@_fixture_record_sanitizer_name_registry.send(:[], ...)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.deregister(klass_or_symbol)
|
32
|
+
@_fixture_record_sanitizer_pattern_registry.delete_if { |pattern, with| with == klass_or_symbol }
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.sanitize_pattern(pattern, with:)
|
36
|
+
@_fixture_record_sanitizer_pattern_registry << [pattern, with]
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.register_sanitizer(klass, as: nil)
|
40
|
+
@_fixture_record_sanitizer_name_registry[as.to_sym] = klass.new
|
37
41
|
end
|
38
42
|
|
39
43
|
def self.fetch(to_be_matched)
|
40
|
-
|
41
|
-
@_fixture_record_sanitizer_name_registry[to_be_matched]
|
42
|
-
else
|
43
|
-
@_fixture_record_sanitizer_pattern_registry.select { |pattern, value| to_be_matched.match(pattern) }.values
|
44
|
-
end
|
44
|
+
@_fixture_record_sanitizer_pattern_registry.select { |pattern, value| to_be_matched.match(pattern) }.map(&:last)
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
@@ -5,7 +5,6 @@ module FixtureRecord
|
|
5
5
|
value.to_s
|
6
6
|
end
|
7
7
|
end
|
8
|
-
FixtureRecord.registry.register_sanitizer(FixtureRecord::Sanitizers::SimpleTimestamp,
|
8
|
+
FixtureRecord.registry.register_sanitizer(FixtureRecord::Sanitizers::SimpleTimestamp, as: :simple_timestamp)
|
9
9
|
end
|
10
10
|
end
|
11
|
-
|
data/lib/fixture_record.rb
CHANGED
@@ -7,10 +7,15 @@ require "fixture_record/association_traversal"
|
|
7
7
|
require "fixture_record/filterable_attributes"
|
8
8
|
require "fixture_record/belongs_to_updation"
|
9
9
|
require "fixture_record/sanitizable"
|
10
|
+
require "fixture_record/configuration"
|
10
11
|
|
11
12
|
module FixtureRecord
|
12
13
|
extend ActiveSupport::Concern
|
13
|
-
mattr_accessor :_locked_by,
|
14
|
+
mattr_accessor :_locked_by,
|
15
|
+
:cache,
|
16
|
+
:data
|
17
|
+
|
18
|
+
mattr_accessor :naming, default: FixtureRecord::Naming::Base.new
|
14
19
|
|
15
20
|
included do
|
16
21
|
attr_accessor :_fixture_record_attributes
|
@@ -33,7 +38,17 @@ module FixtureRecord
|
|
33
38
|
FixtureRecord.release!(self)
|
34
39
|
end
|
35
40
|
|
41
|
+
|
36
42
|
class << self
|
43
|
+
def configure
|
44
|
+
yield config
|
45
|
+
end
|
46
|
+
|
47
|
+
def config
|
48
|
+
@@config ||= Configuration.new
|
49
|
+
end
|
50
|
+
|
51
|
+
|
37
52
|
def lock!(owner)
|
38
53
|
return if locked?
|
39
54
|
|
@@ -4,12 +4,18 @@ module FixtureRecord::Generators
|
|
4
4
|
class InstallGenerator < ::Rails::Generators::Base
|
5
5
|
source_root File.expand_path("templates", __dir__)
|
6
6
|
|
7
|
-
def create_fixture_record_schema
|
8
|
-
# TODO - implement the generator
|
9
|
-
end
|
10
|
-
|
11
7
|
def create_initializer
|
12
|
-
|
8
|
+
application(nil, env: :development) do
|
9
|
+
<<-TXT
|
10
|
+
# By default, fixture_record will only inject itself in the development environment.
|
11
|
+
# If you want it available in `test` or `production` (or other environments), please add
|
12
|
+
# this require line to those environment ruby files. Alternatively, if you want it to
|
13
|
+
# always be loaded, you can relocate the generated `fixture_record/initializer.rb` to
|
14
|
+
# `app/config/initializers/fixture_record.rb` or require this file in `config/application.rb`.
|
15
|
+
require Rails.root.join('fixture_record', 'initializer.rb')\n
|
16
|
+
TXT
|
17
|
+
end
|
18
|
+
template "initializer.rb", Rails.root.join("fixture_record/initializer.rb")
|
13
19
|
end
|
14
20
|
end
|
15
21
|
end
|
@@ -1,9 +1,16 @@
|
|
1
|
-
|
2
|
-
#
|
1
|
+
FixtureRecord.configure do |config|
|
2
|
+
# To customize how fixtures are named, provide a class the responds to #call or a Proc.
|
3
|
+
# The naming object will receive the record and should return a String
|
4
|
+
# config.name_records_with = FixtureRecord::Naming::Base
|
3
5
|
|
4
|
-
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
# Create and register custom sanitizers to format, sanitiize, obfuscate, etc. the data before it is
|
7
|
+
# turned into a test fixture. Regex patterns are used to determine if a column should be passed to a
|
8
|
+
# sanitizer. The regex pattern that is tested is Classname.column_name - so if a sanitizer needs to be
|
9
|
+
# scoped to a specific class only, simply add the classname to the pattern, for example /User.phone_number/
|
10
|
+
# would sanitize the phone_number field for a User but not the phone_number field for a Customer.
|
11
|
+
# If there are other timestamp columns being used throughout your application, you can added them to this list.
|
12
|
+
config.sanitize_pattern /created_at$|updated_at$/, with: :simple_timestamp
|
13
|
+
|
14
|
+
# Inject FixtureRecord concern into ActiveRecord
|
15
|
+
ActiveRecord::Base.include(FixtureRecord)
|
9
16
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fixture_record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2.pre.rc
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brad Schrag
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-03-
|
11
|
+
date: 2024-03-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -39,6 +39,7 @@ files:
|
|
39
39
|
- lib/fixture_record/association_traversal.rb
|
40
40
|
- lib/fixture_record/belongs_to_updation.rb
|
41
41
|
- lib/fixture_record/cache.rb
|
42
|
+
- lib/fixture_record/configuration.rb
|
42
43
|
- lib/fixture_record/data.rb
|
43
44
|
- lib/fixture_record/filterable_attributes.rb
|
44
45
|
- lib/fixture_record/naming.rb
|