diff --git a/lib/faker_maker.rb b/lib/faker_maker.rb index 50e6d96..c2cafa6 100644 --- a/lib/faker_maker.rb +++ b/lib/faker_maker.rb @@ -29,7 +29,10 @@ class NoSuchFactoryError < StandardError; end class NoSuchAttributeError < StandardError; end class ChaosConflictingAttributeError < StandardError; end class NoSuchAttributeNamingStrategy < StandardError; end - # Your code goes here... + + # Token value for marking attributes as omittable from generated JSON. + # Usage in factory definitions: `omit: FakerMaker::OMIT` + OMIT = '__OMIT__'.freeze module_function diff --git a/lib/faker_maker/factory.rb b/lib/faker_maker/factory.rb index b023c02..f1c578e 100644 --- a/lib/faker_maker/factory.rb +++ b/lib/faker_maker/factory.rb @@ -208,6 +208,13 @@ def populate_instance( instance, attr_override_values, chaos: false ) FakerMaker[parent].populate_instance(instance, attr_override_values, chaos:) if parent? attributes = chaos ? chaos_select(chaos) : @attributes + # When chaos is enabled, protect explicitly overridden attributes from being affected by chaos; + # what you pass in is what you get. + if chaos + override_attrs = @attributes.select { |attr| attr_override_values.key?( attr.name ) } + attributes = (attributes + override_attrs).uniq + attributes = @attributes.select { |attr| attributes.include?( attr ) } + end attributes.each do |attribute| value = value_for_attribute( instance, attribute, attr_override_values, chaos: ) diff --git a/lib/faker_maker/version.rb b/lib/faker_maker/version.rb index a497f67..149cf69 100644 --- a/lib/faker_maker/version.rb +++ b/lib/faker_maker/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module FakerMaker - VERSION = '4.0.0' + VERSION = '5.0.0' end diff --git a/spec/faker_maker/attribute_spec.rb b/spec/faker_maker/attribute_spec.rb index 632a48e..c758598 100644 --- a/spec/faker_maker/attribute_spec.rb +++ b/spec/faker_maker/attribute_spec.rb @@ -67,10 +67,22 @@ expect( attr.omit?('anything') ).to be true end - it 'can omit nils and empty' do - attr = FakerMaker::Attribute.new( :my_name, nil, omit: %i[nil empty] ) + it 'can omit using the OMIT token' do + attr = FakerMaker::Attribute.new( :my_name, nil, omit: FakerMaker::OMIT ) + expect( attr.omit?(FakerMaker::OMIT) ).to be true + end + + it 'does not omit nil or empty when using the OMIT token' do + attr = FakerMaker::Attribute.new( :my_name, nil, omit: FakerMaker::OMIT ) + expect( attr.omit?(nil) ).to be false + expect( attr.omit?('') ).to be false + end + + it 'can omit nils, empty and the OMIT token' do + attr = FakerMaker::Attribute.new( :my_name, nil, omit: [:nil, :empty, FakerMaker::OMIT] ) expect( attr.omit?('') ).to be true expect( attr.omit?(nil) ).to be true + expect( attr.omit?(FakerMaker::OMIT) ).to be true expect( attr.omit?('anything') ).to be false end diff --git a/spec/faker_maker/factory_spec.rb b/spec/faker_maker/factory_spec.rb index 4c519da..c3da1fb 100644 --- a/spec/faker_maker/factory_spec.rb +++ b/spec/faker_maker/factory_spec.rb @@ -279,6 +279,24 @@ factory.build( chaos: %i[required_attribute] ) end.to raise_error(FakerMaker::ChaosConflictingAttributeError) end + + it 'preserves explicitly overridden attributes when chaos is enabled' do + factory = FakerMaker::Factory.new( :example_factory ) + required_attribute = FakerMaker::Attribute.new( :required_attribute, proc { 'required' }, required: true ) + optional_attribute = FakerMaker::Attribute.new( :optional_attribute, proc { 'optional' } ) + factory.attach_attribute( required_attribute ) + factory.attach_attribute( optional_attribute ) + FakerMaker.register_factory( factory ) + + fakes = [] + 10.times do + fakes << factory.build( attributes: { optional_attribute: 'overridden' }, chaos: true ) + end + + fakes.each do |fake| + expect( fake.optional_attribute ).to eq 'overridden' + end + end end describe '#instance' do diff --git a/usefakermaker.com.site/site/src/docs/usage/chaos/index.page.md b/usefakermaker.com.site/site/src/docs/usage/chaos/index.page.md index d561490..0206846 100644 --- a/usefakermaker.com.site/site/src/docs/usage/chaos/index.page.md +++ b/usefakermaker.com.site/site/src/docs/usage/chaos/index.page.md @@ -36,3 +36,15 @@ You can also specify which attributes Chaos can use when instantiating your obje ```ruby result = FakerMaker[:item].build( chaos: %i[name manufacturer] ) ``` + +## Overrides are preserved + +When you pass override attributes at build time, Chaos will not touch them. What you pass in is what you get. + +```ruby +result = FakerMaker[:item].build( attributes: { name: 'Specific Name' }, chaos: true ) +result.name +=> "Specific Name" +``` + +Even though `name` is optional and Chaos might otherwise remove it, explicitly overriding it guarantees it will be present with the value you specified. diff --git a/usefakermaker.com.site/site/src/docs/usage/destroying-factories/index.page.md b/usefakermaker.com.site/site/src/docs/usage/destroying-factories/index.page.md index 7e1a5b7..98fafb4 100644 --- a/usefakermaker.com.site/site/src/docs/usage/destroying-factories/index.page.md +++ b/usefakermaker.com.site/site/src/docs/usage/destroying-factories/index.page.md @@ -25,7 +25,7 @@ FakerMaker.factory :user do end FM[:user].as_json - => {:name=>"Patsy Stone"} +=> {:name=>"Patsy Stone"} ``` On the other hand, sometimes you really, really want to destroy a factory and start again (especially if you are experimenting in a REPL for example). FakerMaker allows you to shut a factory which will de-register it from the list of available factories and attempt to unload the class it has built from the Ruby interpreter. @@ -43,7 +43,7 @@ FakerMaker.factory :user do end FM[:user].as_json - => {:name=>"Patsy Stone", :email=>"patsy@fabulous.co.uk"} - ``` +=> {:name=>"Patsy Stone", :email=>"patsy@fabulous.co.uk"} +``` It also provides the `shut_all!` method to remove all factories. diff --git a/usefakermaker.com.site/site/src/docs/usage/getting-started/index.page.md b/usefakermaker.com.site/site/src/docs/usage/getting-started/index.page.md index db2716c..ea6e124 100644 --- a/usefakermaker.com.site/site/src/docs/usage/getting-started/index.page.md +++ b/usefakermaker.com.site/site/src/docs/usage/getting-started/index.page.md @@ -53,5 +53,5 @@ FakerMaker.factory :request do end FakerMaker[:request].build.body -# => nil +=> nil ``` diff --git a/usefakermaker.com.site/site/src/docs/usage/json-field-names/index.page.md b/usefakermaker.com.site/site/src/docs/usage/json-field-names/index.page.md index b135ec8..2451328 100644 --- a/usefakermaker.com.site/site/src/docs/usage/json-field-names/index.page.md +++ b/usefakermaker.com.site/site/src/docs/usage/json-field-names/index.page.md @@ -23,7 +23,7 @@ v.engine_capacity = 125 v.to_json -=> "{\"wheels\":4,\"colour\":\"blue\",\"engineCapacity\":125}" + => "{\"wheels\":4,\"colour\":\"blue\",\"engineCapacity\":125}" ``` ## Per-attribute naming diff --git a/usefakermaker.com.site/site/src/docs/usage/omitting-fields/index.page.md b/usefakermaker.com.site/site/src/docs/usage/omitting-fields/index.page.md index 5d306de..6ee26a0 100644 --- a/usefakermaker.com.site/site/src/docs/usage/omitting-fields/index.page.md +++ b/usefakermaker.com.site/site/src/docs/usage/omitting-fields/index.page.md @@ -12,17 +12,18 @@ end FM[:user].build.as_json => {:name=>"Patsy Stone", :email=>"patsy@fabulous.co.uk", :admin=>false} -FM[:user].build(email: nil).as_json +FM[:user].build( attributes: { email: nil } ).as_json => {:name=>"Patsy Stone", :admin=>false} ``` The `omit` modifier can take a single value or an array. If it is passed a value and the attribute equals this value, it will not be included in the output from `as_json` (which returns a Ruby Hash) or in `to_json` methods. -There are three special modifiers: +There are four special modifiers: -* `:nil` (symbol) to omit output when the attribute is set to nil -* `:empty` to omit output when the value is an empty string, an empty array or an empty hash +* `:nil` (symbol) to omit output when the attribute is set to nil. +* `:empty` to omit output when the value is an empty string, an empty array or an empty hash. * `:always` to never output this attribute. +* `FakerMaker::OMIT` to omit output only when the attribute is set to this token value. These can be mixed with real values, e.g. @@ -33,3 +34,26 @@ FakerMaker.factory :user do admin {false} end ``` + +## Using the omit token + +You may want to exclude a field but still allow it to be set to `nil`, an empty string/array/hash, or any other value. + +`FakerMaker::OMIT` is a token value that can provide this flexibility, e.g. + +```ruby +FakerMaker.factory :user do + name(omit: FakerMaker::OMIT) {'Patsy Stone'} + email(omit: :nil) {'patsy@fabulous.co.uk'} + admin(omit: :empty) {false} +end + +FM[:user].build( attributes: { name: FakerMaker::OMIT } ).as_json +=> {:email=>"patsy@fabulous.co.uk", :admin=>false} + +FM[:user].build( attributes: { name: nil, email: nil } ).as_json +=> {:name=>nil, :admin=>false} + +FM[:user].build( attributes: { name: '', admin: '' } ).as_json +=> {:name=>"", :email=>"patsy@fabulous.co.uk"} +```