faker_maker 1.3.0 → 2.0.0

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: 6355e061188d88ab10d97dae93595abccbf2a73346b6220c265c9241198f93ba
4
- data.tar.gz: 487700e0d8e88f4fb69d00796bb4d644263e7c91af91eea188caa7eaf82861c5
3
+ metadata.gz: cc9201db222f65f8df79f92edc882d9d76e5ca21c6220b377f5e86ce9414d287
4
+ data.tar.gz: d4601f347ce1231fd287d33addde2bb83c00a1a770783eec8007dd7417c6c2f3
5
5
  SHA512:
6
- metadata.gz: e2e50d6ffa61e8164163d2157a65d8a3b93b24e442028e0e04951b25f8e298693bd3a8c6ff3ff948442c38c972f427ec05017020dd120b0cfeefdbb02ebd82b8
7
- data.tar.gz: 4c1ae45972c019f0929b1c0337b20ebe4f92ecaa040811f515934d50ce38e826956c994a3043d1f8a27928921930a32980d4bff29fceffbb0c8a7a82f1d4ebd6
6
+ metadata.gz: ce1be4538d82898bb1accd89585485e6072974350088261dece5f0481a3d1950cbf1462dba6363231e2b25019632d8f1aa649e04cab5c4a0a752c6ca9e428c7f
7
+ data.tar.gz: 2d442d69c483b0aa5ba5f7bc9fd26396376edbc2ff700c9d47305d27f2fc809d42d4971bf8ef8b09c67f7f4912395e6eb0127c0d105e6763708f342916e2ae85
@@ -0,0 +1,37 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+ # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
+ # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
+
8
+ name: Ruby
9
+
10
+ on:
11
+ push:
12
+ pull_request:
13
+ branches: [ "master" ]
14
+
15
+ permissions:
16
+ contents: read
17
+
18
+ jobs:
19
+ test:
20
+
21
+ runs-on: ubuntu-latest
22
+ strategy:
23
+ matrix:
24
+ ruby-version: ["3.0", "3.1", "3.2"]
25
+
26
+ steps:
27
+ - uses: actions/checkout@v3
28
+ - name: Set up Ruby
29
+ # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
30
+ # change this to (see https://github.com/ruby/setup-ruby#versioning):
31
+ # uses: ruby/setup-ruby@v1
32
+ uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0
33
+ with:
34
+ ruby-version: ${{ matrix.ruby-version }}
35
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
36
+ - name: Run tests
37
+ run: bundle exec rake
data/.gitignore CHANGED
@@ -14,4 +14,5 @@ Gemfile.lock
14
14
  .DS_Store
15
15
 
16
16
  faker_maker-*.gem
17
- test.rb
17
+ test.rb
18
+ .idea
data/.rubocop.yml CHANGED
@@ -45,5 +45,11 @@ Metrics/MethodLength:
45
45
  Lint/MissingSuper:
46
46
  Enabled: false
47
47
 
48
+ Metrics/CyclomaticComplexity:
49
+ Max: 10
50
+
51
+ Metrics/PerceivedComplexity:
52
+ Max: 10
53
+
48
54
  AllCops:
49
55
  NewCops: enable
data/bin/console CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  require 'bundler/setup'
5
5
  require 'faker_maker'
6
+ require 'faker'
6
7
 
7
8
  # You can add fixtures and/or initialization code here to make experimenting
8
9
  # with your gem easier. You can also use a different console, if you like.
@@ -0,0 +1,45 @@
1
+ ---
2
+ layout: default
3
+ title: Chaos
4
+ parent: Usage
5
+ nav_order: 11
6
+ ---
7
+
8
+ # Chaos
9
+
10
+ Chaos mode introduces extra spice to your generated factories.
11
+
12
+ Attributes can be marked as either `required` or `optional`, which Chaos will use to determine what attributes are included when instantiating your factory.
13
+
14
+ Required attributes will always be present, however, optional attributes are not guaranteed to be present when Chaos is enabled.
15
+
16
+ *All attributes are optional by default.*
17
+
18
+ To explicitly mark attributes as either required or optional:
19
+
20
+ ```ruby
21
+ FM.factory :item, naming: :json do
22
+ name { 'Blanket' }
23
+ price(required: true) { 100 }
24
+ description(optional: true) { 'Keeps you warm and cozy' }
25
+ manufacturer(optional: 0.7) { 'A large fruit company' }
26
+ end
27
+ ```
28
+
29
+ You can state an attribute is optional using the `optional` option set to either be a `Boolean`, `Integer` or a `Float`.
30
+
31
+ When optional is set to either an `Integer` or a `Float`, this overrides the weighting which Chaos uses to determine the likelihood that attribute will be removed.
32
+
33
+ Higher the value, the more likely that attribute will be present. By default there's a 50/50 chance an optional attribute will be present.
34
+
35
+ To unleash Chaos over a factory, you need to enable it when instantiating your object:
36
+
37
+ ```ruby
38
+ result = FakerMaker[:item].build( chaos: true )
39
+ ```
40
+
41
+ You can also specify which attributes Chaos can use when instantiating your object:
42
+
43
+ ```ruby
44
+ result = FakerMaker[:item].build( chaos: %i[name manufacturer] )
45
+ ```
data/faker_maker.gemspec CHANGED
@@ -44,7 +44,7 @@ Gem::Specification.new do |spec|
44
44
  spec.add_dependency 'activesupport', '>= 5.2', '< 8'
45
45
 
46
46
  spec.add_development_dependency 'bundler', '~> 2.0'
47
- spec.add_development_dependency 'faker', '~> 2.1'
47
+ spec.add_development_dependency 'faker', '~> 3.2'
48
48
  spec.add_development_dependency 'guard', '~> 2.16'
49
49
  spec.add_development_dependency 'guard-bundler', '~> 3.0'
50
50
  spec.add_development_dependency 'guard-rubocop', '~> 1.3'
@@ -3,7 +3,9 @@
3
3
  module FakerMaker
4
4
  # Attributes describe the fields of classes
5
5
  class Attribute
6
- attr_reader :name, :block, :translation
6
+ attr_reader :name, :block, :translation, :required, :optional, :optional_weighting
7
+
8
+ DEFAULT_OPTIONAL_WEIGHTING = 0.5
7
9
 
8
10
  def initialize( name, block = nil, options = {} )
9
11
  assert_valid_options options
@@ -13,6 +15,13 @@ module FakerMaker
13
15
  @translation = options[:json]
14
16
  @omit = *options[:omit]
15
17
  @array = options[:array] == true
18
+
19
+ if options[:required].to_s.downcase.eql?('true') || options[:optional].to_s.downcase.eql?('false')
20
+ @required = true
21
+ else
22
+ @optional = true
23
+ @optional_weighting = determine_optional_weighting(options[:optional])
24
+ end
16
25
  end
17
26
 
18
27
  def array?
@@ -49,7 +58,18 @@ module FakerMaker
49
58
  end
50
59
 
51
60
  def assert_valid_options( options )
52
- options.assert_valid_keys :has, :array, :json, :omit
61
+ options.assert_valid_keys :has, :array, :json, :omit, :required, :optional
62
+ end
63
+
64
+ def determine_optional_weighting( value )
65
+ case value
66
+ when Float
67
+ value.between?(0, 1) ? value : (value / 100)
68
+ when Integer
69
+ value.ceil.between?(0, 100) ? (value.to_f / 100) : DEFAULT_OPTIONAL_WEIGHTING
70
+ else
71
+ DEFAULT_OPTIONAL_WEIGHTING
72
+ end
53
73
  end
54
74
  end
55
75
  end
@@ -5,7 +5,7 @@ module FakerMaker
5
5
  # Factories construct instances of a fake
6
6
  class Factory
7
7
  include Auditable
8
- attr_reader :name, :class_name, :parent
8
+ attr_reader :name, :class_name, :parent, :chaos_selected_attributes
9
9
 
10
10
  def initialize( name, options = {} )
11
11
  assert_valid_options options
@@ -19,7 +19,7 @@ module FakerMaker
19
19
  when nil
20
20
  nil
21
21
  else
22
- raise FakerMaker::NoSuchAttributeNamingStrategy, opttions[:naming]
22
+ raise FakerMaker::NoSuchAttributeNamingStrategy, options[:naming]
23
23
  end
24
24
  @attributes = []
25
25
  @klass = nil
@@ -42,11 +42,22 @@ module FakerMaker
42
42
  @instance ||= instantiate
43
43
  end
44
44
 
45
- def build( attributes = {} )
45
+ def build( attributes: {}, chaos: false, **kwargs )
46
+ if kwargs.present?
47
+ validate_deprecated_build(kwargs)
48
+ attributes = kwargs
49
+ end
50
+
46
51
  @instance = nil
47
52
  before_build if respond_to? :before_build
48
53
  assert_only_known_attributes_for_override( attributes )
49
- populate_instance instance, attributes
54
+
55
+ assert_chaos_options chaos if chaos
56
+
57
+ optional_attributes
58
+ required_attributes
59
+
60
+ populate_instance instance, attributes, chaos
50
61
  yield instance if block_given?
51
62
  after_build if respond_to? :after_build
52
63
  audit(@instance) if FakerMaker.configuration.audit?
@@ -110,11 +121,14 @@ module FakerMaker
110
121
 
111
122
  protected
112
123
 
113
- def populate_instance( instance, attr_override_values )
114
- FakerMaker[parent].populate_instance instance, attr_override_values if parent?
115
- @attributes.each do |attr|
116
- value = value_for_attribute( instance, attr, attr_override_values )
117
- instance.send "#{attr.name}=", value
124
+ def populate_instance( instance, attr_override_values, chaos )
125
+ FakerMaker[parent].populate_instance instance, attr_override_values, chaos if parent?
126
+
127
+ attributes = chaos ? chaos_select(chaos) : @attributes
128
+
129
+ attributes.each do |attribute|
130
+ value = value_for_attribute( instance, attribute, attr_override_values )
131
+ instance.send "#{attribute.name}=", value
118
132
  end
119
133
  instance.instance_variable_set( :@fm_factory, self )
120
134
  end
@@ -128,6 +142,19 @@ module FakerMaker
128
142
  raise FakerMaker::NoSuchAttributeError, issue unless unknown_attrs.empty?
129
143
  end
130
144
 
145
+ def assert_only_known_and_optional_attributes_for_chaos( chaos_attr_values )
146
+ chaos_attr_values = chaos_attr_values.map(&:to_sym)
147
+ unknown_attrs = chaos_attr_values - attribute_names
148
+ issue = "Can't build an instance of '#{class_name}' " \
149
+ "setting '#{unknown_attrs.join( ', ' )}', no such attribute(s)"
150
+ raise FakerMaker::NoSuchAttributeError, issue unless unknown_attrs.empty?
151
+
152
+ # Are any chaos attributes marked as required?
153
+ conflicting_attributes = chaos_attr_values.select { |attr| required_attributes.map(&:name).include? attr }
154
+ issue = "Can't use chaos on a required attribute: '#{conflicting_attributes}'"
155
+ raise FakerMaker::ChaosConflictingAttributeError, issue unless conflicting_attributes.empty?
156
+ end
157
+
131
158
  def attribute_hash_overridden_value?( attr, attr_override_values )
132
159
  attr_override_values.keys.include?( attr.name )
133
160
  end
@@ -164,6 +191,68 @@ module FakerMaker
164
191
  def assert_valid_options( options )
165
192
  options.assert_valid_keys :class, :parent, :naming
166
193
  end
194
+
195
+ # Asserts attributes passed in for chaos mode are valid
196
+ def assert_chaos_options( chaos )
197
+ eval = -> { [Array, String, TrueClass, FalseClass, Symbol].include? chaos.class }
198
+ msg = "chaos: arg does not support object of type: '#{chaos.class}'"
199
+ raise NoSuchAttributeError, msg unless eval.call
200
+
201
+ case chaos
202
+ when Array
203
+ assert_only_known_and_optional_attributes_for_chaos(chaos)
204
+ when String, Symbol
205
+ assert_only_known_and_optional_attributes_for_chaos([chaos])
206
+ end
207
+ end
208
+
209
+ # Selects required @attributes
210
+ def required_attributes
211
+ @required_attributes ||= @attributes.select { |attr| attr.required.eql? true }
212
+ end
213
+
214
+ # Selects optional @attributes
215
+ def optional_attributes
216
+ @optional_attributes ||= @attributes.select(&:optional)
217
+ end
218
+
219
+ # Randomly selects optional attributes
220
+ # Attributes selected from parent will also be selected for the child
221
+ # @param [Array || TrueClass] chaos_attrs
222
+ # @return [Array]
223
+ def chaos_select( chaos_attrs = [] )
224
+ selected_attrs = []
225
+ optional_attrs = optional_attributes.dup
226
+
227
+ # Filter specific optional attributes if present
228
+ if chaos_attrs.is_a?(Array) && chaos_attrs.size.positive?
229
+ optional_attrs, selected_attrs = optional_attrs.partition { |attr| chaos_attrs.include?(attr.name) }
230
+ end
231
+
232
+ # Grab parent selected attributes
233
+ @chaos_selected_attributes = parent? ? FakerMaker[parent].chaos_selected_attributes : []
234
+ selected_inherited_attr = optional_attrs.select do |attr|
235
+ @chaos_selected_attributes.map(&:name).include? attr.name
236
+ end
237
+
238
+ # Select optional attributes based on weighting
239
+ optional_attrs.each do |optional_attr|
240
+ selected_attrs.push(optional_attr) if Random.rand < optional_attr.optional_weighting
241
+ end
242
+
243
+ # Concat required, selected and parent attributes
244
+ @chaos_selected_attributes.concat(required_attributes)
245
+ .concat(selected_inherited_attr)
246
+ .concat(selected_attrs).uniq!
247
+ @chaos_selected_attributes
248
+ end
249
+
250
+ def validate_deprecated_build(kwargs)
251
+ usage = kwargs.each_with_object([]) { |kwarg, result| result << "#{kwarg.first}: #{kwarg.last}" }.join(', ')
252
+
253
+ warn "[DEPRECATION] `FM[:#{name}].build(#{usage})` is deprecated. " \
254
+ "Please use `FM[:#{name}].build(attributes: { #{usage} })` instead."
255
+ end
167
256
  end
168
257
  end
169
258
  # rubocop:enable Metrics/ClassLength
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FakerMaker
4
- VERSION = '1.3.0'
4
+ VERSION = '2.0.0'
5
5
  end
data/lib/faker_maker.rb CHANGED
@@ -27,6 +27,7 @@ module FakerMaker
27
27
  class Error < StandardError; end
28
28
  class NoSuchFactoryError < StandardError; end
29
29
  class NoSuchAttributeError < StandardError; end
30
+ class ChaosConflictingAttributeError < StandardError; end
30
31
  class NoSuchAttributeNamingStrategy < StandardError; end
31
32
  # Your code goes here...
32
33
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: faker_maker
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nigel Brookes-Thomas
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-05-15 00:00:00.000000000 Z
11
+ date: 2023-06-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -50,14 +50,14 @@ dependencies:
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '2.1'
53
+ version: '3.2'
54
54
  type: :development
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: '2.1'
60
+ version: '3.2'
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: guard
63
63
  requirement: !ruby/object:Gem::Requirement
@@ -179,6 +179,7 @@ extensions: []
179
179
  extra_rdoc_files: []
180
180
  files:
181
181
  - ".github/dependabot.yml"
182
+ - ".github/workflows/ruby.yml"
182
183
  - ".gitignore"
183
184
  - ".rspec"
184
185
  - ".rubocop.yml"
@@ -202,6 +203,7 @@ files:
202
203
  - docs/usage/arrays.md
203
204
  - docs/usage/audit_logs.md
204
205
  - docs/usage/building_instances.md
206
+ - docs/usage/chaos.md
205
207
  - docs/usage/dependencies.md
206
208
  - docs/usage/destroying_factories.md
207
209
  - docs/usage/embedding_factories.md