fixture_record 0.1.1.pre.rc → 0.1.2.pre.rc

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6720df231526a9d9119a2146337d2c029e5c1479eaea6f88b17df9ce8e1222fc
4
- data.tar.gz: '0579da7648101dbdf1f3ca02add8ac87a23192fb2ba635ac6749f87e394d062e'
3
+ metadata.gz: e0b73f48614260bc6e1dc1f0ecebcabe4b5bb44779eb3ba807be3942fd8439fc
4
+ data.tar.gz: 2af43ba19fd5cde723d3dede76fd21eecd17f92d1ad38693702d14fbd85939fa
5
5
  SHA512:
6
- metadata.gz: 1de630e483468ae70fb9e41351f5607fee4516ffea36f9008860a801c7a95c2347d608bf2c6d50c559d302eac7eb0deb2d606cd71e4914e8c8728e09d3bb60d1
7
- data.tar.gz: 6d21e6b3cd38b97122fdf567df30b9d657df3a7787e481ed79343dd691edfd48882b353f96fb3edc2693b7336b67e5c2448ec75bd324dd61c0453708ad5ea7e4
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 @source_record.respond_to?(@symbol)
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
- [fixture_record_prefix, self.class.model_name.param_key, (self.id || 'new'), fixture_record_suffix].compact_blank.join '_'
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.register_sanitizer(klass, *patterns, as: nil)
31
- if as
32
- @_fixture_record_sanitizer_name_registry[as.to_sym] = klass.new
33
- end
34
- patterns.each do |pattern|
35
- @_fixture_record_sanitizer_pattern_registry[pattern] = klass.new
36
- end
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
- if @_fixture_record_sanitizer_name_registry.key?(to_be_matched)
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, /created_at$|updated_at$/, as: :simple_timestamp)
8
+ FixtureRecord.registry.register_sanitizer(FixtureRecord::Sanitizers::SimpleTimestamp, as: :simple_timestamp)
9
9
  end
10
10
  end
11
-
@@ -1,3 +1,3 @@
1
1
  module FixtureRecord
2
- VERSION = "0.1.1-rc"
2
+ VERSION = "0.1.2-rc"
3
3
  end
@@ -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, :cache, :data
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
- template "initializer.rb", Rails.root.join("config/initializers/fixture_record.rb")
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
- # FixtureRecord is currently only intended to be loaded in a development environment.
2
- # Change this conditional at your own risk if you want it to load in production.
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
- if Rails.env.development?
5
- # Inject FixtureRecord after active_record loads
6
- ActiveSupport.on_load(:active_record) do
7
- ActiveRecord::Base.include(FixtureRecord)
8
- end
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.1.pre.rc
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-28 00:00:00.000000000 Z
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