faker_maker 3.0.0 → 4.0.0

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.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +1 -1
  3. data/.gitignore +4 -0
  4. data/.rubocop.yml +1 -0
  5. data/Gemfile +2 -0
  6. data/MIGRATION_NOTES.md +47 -0
  7. data/TODO.md +16 -0
  8. data/faker_maker.gemspec +1 -1
  9. data/hack/nested.rb +21 -0
  10. data/lib/faker_maker/attribute.rb +10 -1
  11. data/lib/faker_maker/base.rb +2 -2
  12. data/lib/faker_maker/definition_proxy.rb +2 -2
  13. data/lib/faker_maker/factory.rb +122 -34
  14. data/lib/faker_maker/version.rb +1 -1
  15. data/usefakermaker.com/pages/about/index.md +20 -0
  16. data/usefakermaker.com.site/.gitignore +2 -0
  17. data/usefakermaker.com.site/site/assets/favicons/android-chrome-192x192.png +0 -0
  18. data/usefakermaker.com.site/site/assets/favicons/android-chrome-512x512.png +0 -0
  19. data/usefakermaker.com.site/site/assets/favicons/apple-touch-icon.png +0 -0
  20. data/usefakermaker.com.site/site/assets/favicons/favicon-16x16.png +0 -0
  21. data/usefakermaker.com.site/site/assets/favicons/favicon-32x32.png +0 -0
  22. data/usefakermaker.com.site/site/assets/favicons/favicon.ico +0 -0
  23. data/usefakermaker.com.site/site/assets/favicons/site.webmanifest +19 -0
  24. data/usefakermaker.com.site/site/assets/robots.txt +2 -0
  25. data/usefakermaker.com.site/site/assets/styles.css +351 -0
  26. data/{usefakermaker.com/docs/contributing/index.md → usefakermaker.com.site/site/src/docs/contributing/index.page.md} +1 -5
  27. data/{usefakermaker.com/docs/installing/index.md → usefakermaker.com.site/site/src/docs/installing/index.page.md} +1 -5
  28. data/{usefakermaker.com/docs/usage/arrays/index.md → usefakermaker.com.site/site/src/docs/usage/arrays/index.page.md} +1 -4
  29. data/{usefakermaker.com/docs/usage/building-instances/index.md → usefakermaker.com.site/site/src/docs/usage/building-instances/index.page.md} +6 -7
  30. data/{usefakermaker.com/docs/usage/chaos/index.md → usefakermaker.com.site/site/src/docs/usage/chaos/index.page.md} +1 -4
  31. data/{usefakermaker.com/docs/usage/destroying-factories/index.md → usefakermaker.com.site/site/src/docs/usage/destroying-factories/index.page.md} +1 -4
  32. data/{usefakermaker.com/docs/usage/embedding-factories/index.md → usefakermaker.com.site/site/src/docs/usage/embedding-factories/index.page.md} +41 -7
  33. data/{usefakermaker.com/docs/usage/getting-started/index.md → usefakermaker.com.site/site/src/docs/usage/getting-started/index.page.md} +1 -4
  34. data/{usefakermaker.com/docs/usage/history-logging/index.md → usefakermaker.com.site/site/src/docs/usage/history-logging/index.page.md} +1 -6
  35. data/{usefakermaker.com/docs/usage/inheritance/index.md → usefakermaker.com.site/site/src/docs/usage/inheritance/index.page.md} +1 -4
  36. data/{usefakermaker.com/docs/usage/json-field-names/index.md → usefakermaker.com.site/site/src/docs/usage/json-field-names/index.page.md} +1 -4
  37. data/{usefakermaker.com/docs/usage/lifecycle-hooks/index.md → usefakermaker.com.site/site/src/docs/usage/lifecycle-hooks/index.page.md} +1 -4
  38. data/{usefakermaker.com/docs/usage/managing-dependencies/index.md → usefakermaker.com.site/site/src/docs/usage/managing-dependencies/index.page.md} +1 -4
  39. data/{usefakermaker.com/docs/usage/omitting-fields/index.md → usefakermaker.com.site/site/src/docs/usage/omitting-fields/index.page.md} +1 -4
  40. data/{usefakermaker.com/pages/index.markdown → usefakermaker.com.site/site/src/index.page.md} +9 -19
  41. data/usefakermaker.com.site/site/templates/_nav.html.erb +27 -0
  42. data/usefakermaker.com.site/site/templates/layout.html.erb +46 -0
  43. data/usefakermaker.com.site/site/templates/page.html.erb +3 -0
  44. metadata +39 -51
  45. data/usefakermaker.com/.gitignore +0 -5
  46. data/usefakermaker.com/404.html +0 -25
  47. data/usefakermaker.com/Gemfile +0 -39
  48. data/usefakermaker.com/README.md +0 -5
  49. data/usefakermaker.com/_config.yml +0 -299
  50. data/usefakermaker.com/_config.yml.orig +0 -55
  51. data/usefakermaker.com/_data/navigation.yml +0 -51
  52. data/usefakermaker.com/_data/ui-text.yml +0 -2132
  53. data/usefakermaker.com/_site/assets/css/main.css +0 -6
  54. data/usefakermaker.com/_site/assets/css/main.css.map +0 -1
  55. data/usefakermaker.com/_site/assets/js/_main.js +0 -230
  56. data/usefakermaker.com/_site/assets/js/lunr/lunr-en.js +0 -69
  57. data/usefakermaker.com/_site/assets/js/lunr/lunr-gr.js +0 -522
  58. data/usefakermaker.com/_site/assets/js/lunr/lunr-store.js +0 -1
  59. data/usefakermaker.com/_site/assets/js/lunr/lunr.js +0 -3475
  60. data/usefakermaker.com/_site/assets/js/lunr/lunr.min.js +0 -6
  61. data/usefakermaker.com/_site/assets/js/main.min.js +0 -7
  62. data/usefakermaker.com/_site/assets/js/main.min.js.map +0 -1
  63. data/usefakermaker.com/_site/assets/js/plugins/gumshoe.js +0 -484
  64. data/usefakermaker.com/_site/assets/js/plugins/jquery.ba-throttle-debounce.js +0 -252
  65. data/usefakermaker.com/_site/assets/js/plugins/jquery.fitvids.js +0 -82
  66. data/usefakermaker.com/_site/assets/js/plugins/jquery.greedy-navigation.js +0 -128
  67. data/usefakermaker.com/_site/assets/js/plugins/jquery.magnific-popup.js +0 -1860
  68. data/usefakermaker.com/_site/assets/js/plugins/smooth-scroll.js +0 -650
  69. data/usefakermaker.com/_site/assets/js/vendor/jquery/jquery-3.6.0.js +0 -10881
  70. data/usefakermaker.com/_site/feed.xml +0 -1
  71. data/usefakermaker.com/_site/robots.txt +0 -1
  72. data/usefakermaker.com/_site/sitemap.xml +0 -3
  73. data/usefakermaker.com/about.markdown +0 -18
  74. /data/{usefakermaker.com → usefakermaker.com.site/site}/assets/images/pug.png +0 -0
  75. /data/{usefakermaker.com → usefakermaker.com.site/site}/assets/images/unipug.svg +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e6d677fff1380c1eddf6c0305b978d8b161d0f5fbb5e1752bc587a0dc10e8554
4
- data.tar.gz: 7df7f5f9279c2e745842999117672e608b9d1e2302d63bcae9914429f796491a
3
+ metadata.gz: 4f341f27ec954b732dde7141c9ad532346c09dadbbed4ea469894b10cac652f8
4
+ data.tar.gz: 61919d74b140d82fba2750bcf0e1c25473ebe21e02a2b213bd1109d94f22d9e2
5
5
  SHA512:
6
- metadata.gz: fe8ae2a8550b5eaf9a78eaf2bda1ab8f7f1eac1f1f557223e744111bf45ee417c5a9215aa1cd2dae5e23d90a1b3b9aa347a2bba52a5d97d7d8f53f91fd629196
7
- data.tar.gz: 19290557db759839023a0a7b80b9cd2bf9f01e652a7e610f5f5cfd9f037459f2be04cc51a34eed271dead74289f9001fd15aebbf8913355e55c5004a7b466f31
6
+ metadata.gz: de3ae09ed6b848612d3fc23533d04f4633eaa4be42f7f7be9fc3f3938f8b86afa2a495f6928a66f38207d63233e2b37ce88897178688a4f3aa2c6c5f524b51e4
7
+ data.tar.gz: cdc3c4509819316bc389d2bf1857883ddfa62b14b93a8f794a043af70bd603dec46f7e0a0c37d29d8814ff447ac2762cd3d56a92fc917338bc3458b4113263c9
@@ -20,7 +20,7 @@ jobs:
20
20
  runs-on: ubuntu-latest
21
21
  strategy:
22
22
  matrix:
23
- ruby-version: ["3.0", "3.1", "3.2", "3.3", "3.4"]
23
+ ruby-version: ["3.4", "4.0"]
24
24
 
25
25
  steps:
26
26
  - uses: actions/checkout@v3
data/.gitignore CHANGED
@@ -8,6 +8,10 @@
8
8
  /tmp/
9
9
 
10
10
  Gemfile.lock
11
+
12
+ /hack/
13
+ TODO.md
14
+
11
15
  # rspec failure tracking
12
16
  .rspec_status
13
17
 
data/.rubocop.yml CHANGED
@@ -52,4 +52,5 @@ Metrics/PerceivedComplexity:
52
52
  Max: 10
53
53
 
54
54
  AllCops:
55
+ TargetRubyVersion: 4.0
55
56
  NewCops: enable
data/Gemfile CHANGED
@@ -10,3 +10,5 @@ gemspec
10
10
  gem 'bundler-audit', '~> 0.9.2'
11
11
  gem 'irb', '~> 1.15'
12
12
  gem 'rdoc', '~> 6.13'
13
+
14
+ gem 'awesome_print', '~> 1.9'
@@ -0,0 +1,47 @@
1
+ # Jekyll to Jackdaw Migration Notes
2
+
3
+ ## 1. Project Initialization
4
+ * Run `jackdaw new <project_name>` to generate the standard directory structure:
5
+ * `site/src`: Content
6
+ * `site/templates`: ERB Templates
7
+ * `site/assets`: Static files (images, css, favicons)
8
+
9
+ ## 2. Template Conversion
10
+ * **Layouts**: Consolidate Jekyll layouts (default, page, etc.) into `site/templates/layout.html.erb`.
11
+ * Replace `{{ content }}` with `<%= content %>`.
12
+ * Replace `{{ page.title }}` with `<%= title %>`.
13
+ * **Partials**: Convert `{% include file.html %}` to `<%= render 'file' %>`.
14
+ * Rename partial files to start with an underscore (e.g., `_nav.html.erb`).
15
+ * **Assets**:
16
+ * Move CSS/JS/Images to `site/assets/`.
17
+ * **Important**: When referencing in HTML, omit `assets/`.
18
+ * `site/assets/styles.css` -> `<link href="/styles.css">`
19
+ * `site/assets/img/logo.png` -> `<img src="/img/logo.png">`
20
+
21
+ ## 3. Content Migration Strategies
22
+ * **File Extensions**:
23
+ * Regular pages: `.md` -> `.page.md`
24
+ * Blog posts: `.md` -> `.blog.md` (Filename must start with `YYYY-MM-DD-`)
25
+ * **Frontmatter Handling**:
26
+ * Jackdaw renders the *first H1* as the page title.
27
+ * **Task**: Strip YAML frontmatter (`--- ... ---`) and ensure the file starts with `# Page Title`.
28
+ * **Directory Structure**:
29
+ * Keep the same folder structure in `site/src` to maintain URLs.
30
+ * **Conflict Warning**: Do not create `topic.page.md` if you also have a folder named `topic/`. Instead, create `topic/index.page.md`.
31
+
32
+ ## 4. Automation (Ruby Script)
33
+ For bulk migration of documentation, use a script to:
34
+ 1. Iterate through source directories.
35
+ 2. Read file content.
36
+ 3. specific YAML frontmatter values (Title).
37
+ 4. Rewrite file to destination with new header and `.page.md` extension.
38
+
39
+ ## 5. Navigation & Indices
40
+ * Jackdaw does not automatically build navigation menus from folder structure.
41
+ * **Manual**: Edit `site/templates/_nav.html.erb`.
42
+ * **Auto-generated Indices**: Write scripts to generate index markdown files (e.g., "List of all Rules") if the content changes frequently.
43
+
44
+ ## 6. Styling
45
+ * Jackdaw provides no default styling.
46
+ * Create a robust `styles.css` using CSS Variables for theming.
47
+ * Link fonts and stylesheets in the `<head>` of `layout.html.erb`.
data/TODO.md ADDED
@@ -0,0 +1,16 @@
1
+ -- DONE - sorta
2
+ 2. get attributes including embedded attr -- THIS WORKS IF USING THE FACTORY PARAMETER, NOT PROC EMBEDDED FACTORIES -- NEED A CHANGE TO OSL FACTORIES
3
+ 3. What the hell is going on with @embedded factories -- why is this always nil CHECK -- FOR THE LOVE OF GOD, WRITE DOCUMENTATION FOR THIS
4
+ 4. Factory#attribute_names line ~ 110
5
+
6
+ -- TODO
7
+
8
+ 1. nested builds using the build method -- this is hidden at the moment, it sets the value of the key to a hash rather than recursively calling the field#field method
9
+ 5. FM[:foo].build( attributes: { does_not_exist: 1 }, raise_on_error: false ) # RAISE_ON_ERROR
10
+ 5. FM[:foo].build( attributes: { does_not_exist: 1 }, strict: false ) # RAISE_ON_ERROR
11
+ 5. FM[:foo].build( attributes: { does_not_exist: 1 }, very_strict: false ) # RAISE_ON_ERROR
12
+
13
+
14
+ building embedded factories need to know the build options passed in to the top level build call -- e.g. is chaos on or not
15
+
16
+ check naming strategies still work
data/faker_maker.gemspec CHANGED
@@ -43,7 +43,7 @@ Gem::Specification.new do |spec|
43
43
 
44
44
  spec.add_dependency 'activesupport', '>= 5.2', '< 9'
45
45
 
46
- spec.add_development_dependency 'bundler', '~> 2'
46
+ spec.add_development_dependency 'bundler', '>= 2'
47
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'
data/hack/nested.rb ADDED
@@ -0,0 +1,21 @@
1
+ require 'faker_maker'
2
+ require 'awesome_print'
3
+
4
+ FM.factory :random do
5
+ flavour { 'blue' }
6
+ end
7
+
8
+ FM.factory :item do
9
+ name { 'toothpaste' }
10
+ price { 0.99 }
11
+ end
12
+
13
+ FM.factory :coupon do
14
+ discount { 0.10 }
15
+ end
16
+
17
+ FM.factory :basket do
18
+ items( factory: %i[item random] )
19
+ end
20
+
21
+ # { a: 1, b: { x: 2 } }
@@ -3,7 +3,7 @@
3
3
  module FakerMaker
4
4
  # Attributes describe the fields of classes
5
5
  class Attribute
6
- attr_reader :name, :block, :translation, :required, :optional, :optional_weighting, :embedded_factories
6
+ attr_reader :name, :block, :translation, :required, :optional, :optional_weighting
7
7
 
8
8
  DEFAULT_OPTIONAL_WEIGHTING = 0.5
9
9
 
@@ -25,6 +25,15 @@ module FakerMaker
25
25
  end
26
26
  end
27
27
 
28
+ # Return an array of factory instances
29
+ def embedded_factories
30
+ @embedded_factories.map { |name| FakerMaker[name] }
31
+ end
32
+
33
+ def embedded_factories?
34
+ @embedded_factories.any?
35
+ end
36
+
28
37
  def array?
29
38
  forced_array? || @array
30
39
  end
@@ -3,12 +3,12 @@
3
3
  module FakerMaker
4
4
  # Base module for defining the DSL
5
5
  module Base
6
- def factory(name, options = {}, &block)
6
+ def factory(name, options = {}, &)
7
7
  factory = FakerMaker.find_factory(name)
8
8
  if factory.nil?
9
9
  factory = FakerMaker::Factory.new name, options
10
10
  proxy = DefinitionProxy.new factory
11
- proxy.instance_eval( &block ) if block_given?
11
+ proxy.instance_eval( & ) if block_given?
12
12
  FakerMaker.register_factory factory
13
13
  else
14
14
  factory
@@ -13,8 +13,8 @@ module FakerMaker
13
13
  @factory
14
14
  end
15
15
 
16
- def method_missing(name, *args, &block)
17
- attribute = FakerMaker::Attribute.new name, block, *args
16
+ def method_missing(name, *, &block)
17
+ attribute = FakerMaker::Attribute.new(name, block, *)
18
18
  @factory.attach_attribute attribute
19
19
  end
20
20
 
@@ -5,8 +5,26 @@ module FakerMaker
5
5
  # Factories construct instances of a fake
6
6
  class Factory
7
7
  include Auditable
8
+
8
9
  attr_reader :name, :class_name, :parent, :chaos_selected_attributes
9
10
 
11
+ # Create a new +Factory+ object.
12
+ #
13
+ # This method does not automatically register the factory,see
14
+ # FakerMaker#register_factory
15
+ #
16
+ # Options:
17
+ # - +:class_name+ - override the default class name that FakerMaker will generate.
18
+ # This is useful in the case of collisions with existing classes or keywords.
19
+ # - +:parent+ - the parent factory from which this factory inherits attributes.
20
+ # Instances built by this factory will have a class which inherits from the parent's
21
+ # class.
22
+ # - +:naming+ - one of:
23
+ # - +nil+ (default) - use field names as the method name and in JSON conversion
24
+ # - +:json+ - use field names as the method name but convert when rendering JSON, e.g.
25
+ # +hello_world+ becomes +helloWorld+
26
+ # - +:json_capitalised+ (or +:json_capitalized+) - as +:json+ but with the first letter
27
+ # captialised, e.g. +hello_world+ becomes +HelloWorld+
10
28
  def initialize( name, options = {} )
11
29
  assert_valid_options options
12
30
  @name = name.respond_to?(:to_sym) ? name.to_sym : name.to_s.underscore.to_sym
@@ -26,6 +44,7 @@ module FakerMaker
26
44
  @parent = options[:parent]
27
45
  end
28
46
 
47
+ # Get the Class of the parent for this factory
29
48
  def parent_class
30
49
  if @parent
31
50
  FakerMaker::Factory.const_get( FakerMaker[@parent].class_name )
@@ -34,6 +53,7 @@ module FakerMaker
34
53
  end
35
54
  end
36
55
 
56
+ # Attach a FakerMaker::Attribute to this Factory
37
57
  def attach_attribute( attribute )
38
58
  @attributes << attribute
39
59
  end
@@ -42,14 +62,11 @@ module FakerMaker
42
62
  @instance ||= instantiate
43
63
  end
44
64
 
45
- def build( attributes: {}, chaos: false, **kwargs )
46
- if kwargs.present?
47
- validate_deprecated_build(kwargs)
48
- attributes = kwargs
49
- end
50
-
65
+ def build( attributes: {}, chaos: false )
51
66
  @instance = nil
52
67
  before_build if respond_to? :before_build
68
+
69
+ # TODO: make this cleverer to handle nested attributes
53
70
  assert_only_known_attributes_for_override( attributes )
54
71
 
55
72
  assert_chaos_options chaos if chaos
@@ -57,13 +74,19 @@ module FakerMaker
57
74
  optional_attributes
58
75
  required_attributes
59
76
 
60
- populate_instance instance, attributes, chaos
77
+ populate_instance(instance, attributes, chaos:)
61
78
  yield instance if block_given?
79
+
62
80
  after_build if respond_to? :after_build
63
81
  audit(@instance) if FakerMaker.configuration.audit?
64
82
  instance
65
83
  end
66
84
 
85
+ # Construct a Class object which will become the parent type of objects built
86
+ # by this factory.
87
+ #
88
+ # The Class object will be created and the attributes added to it. The returned value
89
+ # is a Ruby Class which can be instantiated.
67
90
  def assemble
68
91
  if @klass.nil?
69
92
  @klass = Class.new parent_class
@@ -91,7 +114,7 @@ module FakerMaker
91
114
  unless @json_key_map
92
115
  @json_key_map = {}.with_indifferent_access
93
116
  @json_key_map.merge!( FakerMaker[parent].json_key_map ) if parent?
94
- attributes.each_with_object( @json_key_map ) do |attr, map|
117
+ attributes(include_embeddings: false).each_with_object( @json_key_map ) do |attr, map|
95
118
  key = if attr.translation?
96
119
  attr.translation
97
120
  elsif @naming_strategy
@@ -106,29 +129,88 @@ module FakerMaker
106
129
  @json_key_map
107
130
  end
108
131
 
109
- def attribute_names( collection = [] )
110
- collection |= FakerMaker[parent].attribute_names( collection ) if parent?
111
- collection | @attributes.map( &:name )
132
+ # Returns a transformed list of attribute names from the `attributes` array.
133
+ # For each item in the array:
134
+ # - If the item is a Hash, recursively transforms its keys and values,
135
+ # replacing keys with their `name` and applying the same transformation to values.
136
+ # - Otherwise, replaces the item with its `name`.
137
+ #
138
+ # @return [Array] An array (possibly nested) of attribute names, with hashes' keys replaced by their `name`.
139
+ def attribute_names
140
+ transform = lambda do |arr|
141
+ arr.map do |item|
142
+ if item.is_a?(Hash)
143
+ item.transform_keys(&:name).transform_values { |v| transform.call(v) }
144
+ else
145
+ item.name
146
+ end
147
+ end
148
+ end
149
+ transform.call(attributes)
112
150
  end
113
151
 
114
- def attributes( collection = [] )
152
+ # Returns a collection of attributes for the factory, optionally including embedded factory attributes.
153
+ #
154
+ # @param collection [Array] an optional array of attributes to start with (default: empty array)
155
+ # @param include_embeddings [Boolean] whether to include attributes from embedded factories (default: true)
156
+ # @return [Array] the collection of attributes, possibly including embedded factory attributes as hashes
157
+ #
158
+ # If the factory has a parent, its attributes are merged in. Attributes without embedded factories are added
159
+ # directly. If `include_embeddings` is true, attributes with embedded factories are added as hashes mapping
160
+ # the attribute to the flattened attributes of its embedded factories. If false, only the attribute itself
161
+ # is added.
162
+ def attributes( collection = [], include_embeddings: true )
115
163
  collection |= FakerMaker[parent].attributes( collection ) if parent?
116
- collection | @attributes
164
+ collection |= @attributes.reject { |attr| attr.embedded_factories.any? }
165
+
166
+ # if there is an embedded factory(-ies) and we are including the embedded factory's
167
+ # fields, we are going to return a hash
168
+ if include_embeddings
169
+ @attributes.select { |attr| attr.embedded_factories.any? }.each do |attr|
170
+ collection << { attr => attr.embedded_factories.flat_map(&:attributes) }
171
+ end
172
+ # if there is an embedded factory(-ies) and we are not including the embedded factory's
173
+ # fields, just add the attribute into the set of returned fields
174
+ else
175
+ collection |= @attributes.select { |attr| attr.embedded_factories.any? }
176
+ end
177
+
178
+ collection
117
179
  end
118
180
 
181
+ # Finds and returns the first attribute matching the given name.
182
+ #
183
+ # This method searches through the attributes (excluding embeddings) and returns the first attribute
184
+ # whose name, translation, or the result of applying the naming strategy to its name matches the provided `name`.
185
+ #
186
+ # @param name [String] The name to search for among the attributes. Defaults to an empty string.
187
+ # @return [Object, nil] The first matching attribute object, or nil if no match is found.
119
188
  def find_attribute( name = '' )
120
- attributes.filter { |a| [a.name, a.translation, @naming_strategy&.name(a.name)].include? name }.first
189
+ attributes(include_embeddings: false).filter do |a|
190
+ [a.name, a.translation, @naming_strategy&.name(a.name)].include? name
191
+ end.first
121
192
  end
122
193
 
123
194
  protected
124
195
 
125
- def populate_instance( instance, attr_override_values, chaos )
126
- FakerMaker[parent].populate_instance instance, attr_override_values, chaos if parent?
196
+ # Populates the given instance with attribute values, optionally applying chaos/randomization.
197
+ #
198
+ # @param instance [Object] The object instance to populate with attribute values.
199
+ # @param attr_override_values [Hash] A hash of attribute names and their override values.
200
+ # @param chaos [Boolean, Integer, nil] If truthy, enables chaos mode which may randomize or select a subset
201
+ # of attributes.
202
+ # @return [void]
203
+ #
204
+ # If the factory has a parent, its attributes are populated first.
205
+ # Each attribute is assigned a value, either from the override values or generated.
206
+ # The factory instance is set on the populated object for reference.
207
+ def populate_instance( instance, attr_override_values, chaos: false )
208
+ FakerMaker[parent].populate_instance(instance, attr_override_values, chaos:) if parent?
127
209
 
128
210
  attributes = chaos ? chaos_select(chaos) : @attributes
129
211
 
130
212
  attributes.each do |attribute|
131
- value = value_for_attribute( instance, attribute, attr_override_values )
213
+ value = value_for_attribute( instance, attribute, attr_override_values, chaos: )
132
214
  instance.send "#{attribute.name}=", value
133
215
  end
134
216
  instance.instance_variable_set( :@fm_factory, self )
@@ -137,7 +219,10 @@ module FakerMaker
137
219
  private
138
220
 
139
221
  def assert_only_known_attributes_for_override( attr_override_values )
140
- unknown_attrs = attr_override_values.keys - attribute_names
222
+ unknown_attrs = attr_override_values.keys - attribute_names.flat_map do |item|
223
+ item.is_a?(Hash) ? item.keys : item
224
+ end
225
+
141
226
  issue = "Can't build an instance of '#{class_name}' " \
142
227
  "setting '#{unknown_attrs.join( ', ' )}', no such attribute(s)"
143
228
  raise FakerMaker::NoSuchAttributeError, issue unless unknown_attrs.empty?
@@ -156,34 +241,44 @@ module FakerMaker
156
241
  raise FakerMaker::ChaosConflictingAttributeError, issue unless conflicting_attributes.empty?
157
242
  end
158
243
 
159
- def attribute_hash_overridden_value?( attr, attr_override_values )
244
+ def overridden_value?( attr, attr_override_values )
160
245
  attr_override_values.keys.include?( attr.name )
161
246
  end
162
247
 
163
- def value_for_attribute( instance, attr, attr_override_values )
164
- if attribute_hash_overridden_value?( attr, attr_override_values )
248
+ def value_for_attribute( instance, attr, attr_override_values, chaos: false )
249
+ if !attr.embedded_factories? && overridden_value?( attr, attr_override_values )
165
250
  attr_override_values[attr.name]
166
251
  elsif attr.array?
167
252
  [].tap do |a|
168
253
  attr.cardinality.times do
169
- manufacture = manufacture_from_embedded_factory( attr )
170
- # if manufacture has been build and there is a block, instance_exec the block
254
+ manufacture = manufacture_from_embedded_factory( attr, attr_override_values[attr.name.to_sym], chaos: )
255
+ # if manufacture has been built and there is a block, instance_exec the block
171
256
  # otherwise just add the manufacture to the array
172
257
  a << (attr.block ? instance.instance_exec(manufacture, &attr.block) : manufacture)
173
258
  end
174
259
  end
175
260
  else
176
- manufacture = manufacture_from_embedded_factory( attr )
261
+ manufacture = manufacture_from_embedded_factory( attr, attr_override_values[attr.name.to_sym], chaos: )
177
262
  attr.block ? instance.instance_exec(manufacture, &attr.block) : manufacture
178
263
  end
179
264
  end
180
265
 
181
- def manufacture_from_embedded_factory( attr )
266
+ def manufacture_from_embedded_factory( attr, attributes = {}, chaos: false )
267
+ attributes ||= {}
182
268
  # The name of the embedded factory randomly selected from the list of embedded factories.
183
- embedded_factory_name = attr.embedded_factories.sample
269
+ embedded_factory = attr.embedded_factories.sample
270
+
271
+ # filter out attributes for non-chosen embedded factories to avoid triggering
272
+ # the NoSuchAttribute exception
273
+ attributes = attr
274
+ .embedded_factories
275
+ .reject { |e| e == embedded_factory }
276
+ .flat_map { |f| pp f.attributes.map(&:name) }
277
+ .then { |excl| attributes.delete_if { |k, _v| excl.include?(k) } }
278
+
184
279
  # The object that is being manufactured by the factory.
185
280
  # If an embedded factory name is provided, it builds the object using FakerMaker.
186
- embedded_factory_name ? FakerMaker[embedded_factory_name].build : nil
281
+ embedded_factory&.build(attributes:, chaos:)
187
282
  end
188
283
 
189
284
  def instantiate
@@ -264,13 +359,6 @@ module FakerMaker
264
359
  .concat(selected_attrs).uniq!
265
360
  @chaos_selected_attributes
266
361
  end
267
-
268
- def validate_deprecated_build(kwargs)
269
- usage = kwargs.each_with_object([]) { |kwarg, result| result << "#{kwarg.first}: #{kwarg.last}" }.join(', ')
270
-
271
- warn "[DEPRECATION] `FM[:#{name}].build(#{usage})` is deprecated. " \
272
- "Please use `FM[:#{name}].build(attributes: { #{usage} })` instead."
273
- end
274
362
  end
275
363
  end
276
364
  # rubocop:enable Metrics/ClassLength
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FakerMaker
4
- VERSION = '3.0.0'
4
+ VERSION = '4.0.0'
5
5
  end
@@ -0,0 +1,20 @@
1
+ ---
2
+ title: "About"
3
+ layout: single
4
+ permalink: /pages/about/
5
+ author_profile: true
6
+ ---
7
+
8
+ Faker Maker was designed to be a trivial way to create data factories that could throw JSON payloads at an API endpoint. It has grown well beyond that original purpose but still remains a thing for building things that give you data.
9
+
10
+ It is much beloved by me. Although it's a personal project, it's used extensively by my employer and influenced by the needs of my colleagues. I hope it will be useful to you as well. I am very open to ideas, feedback and contributions.
11
+
12
+ Faker Maker is licenced under the [MIT licence](https://raw.githubusercontent.com/BillyRuffian/faker_maker/refs/heads/master/LICENSE.txt). Do with it what you will and have fun.
13
+
14
+ ### What's the Billy Ruffian thing?
15
+
16
+ HMS Bellerophon was a 74-gun third-rate ship of the line of the Royal Navy. Launched in 1786, she served during the French Revolutionary and Napoleonic Wars, mostly on blockades or convoy escort duties. She fought in three fleet actions: the Glorious First of June, the Battle of the Nile and the Battle of Trafalgar. She became famous as the ship upon which Napoleon surrendered and which transported him into exile in 1815.
17
+
18
+ Her sailors, not being educated in the Classics, struggled to pronounce her name and so she became known as the Billy Ruffian and her crew as the "Billy Ruffians". The name stuck and was used as a nickname for the ship for the rest of her career.
19
+
20
+ Since no one in coffee shops can spell my name, I adopted 'Billy' which turned into 'Billy Ruffian'. It's also a bloody good story.
@@ -0,0 +1,2 @@
1
+ public/
2
+ .DS_Store
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "FakerMaker",
3
+ "short_name": "FakerMaker",
4
+ "icons": [
5
+ {
6
+ "src": "/favicons/android-chrome-192x192.png",
7
+ "sizes": "192x192",
8
+ "type": "image/png"
9
+ },
10
+ {
11
+ "src": "/favicons/android-chrome-512x512.png",
12
+ "sizes": "512x512",
13
+ "type": "image/png"
14
+ }
15
+ ],
16
+ "theme_color": "#ffffff",
17
+ "background_color": "#ffffff",
18
+ "display": "standalone"
19
+ }
@@ -0,0 +1,2 @@
1
+ User-agent: *
2
+ Allow: /