foobara 0.5.1 → 0.5.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fbe07dc4815630bc663e2d203a40e6ee507e7a35cd800937ed075c717b349842
4
- data.tar.gz: aa8b0cfe1e3dc73a9d5633a709589b8cd51efea18d901d801a33700bfba390d3
3
+ metadata.gz: 3dedef3834e7b4624c4087f4c551c89ccd89eb7b775da12248142fa85b113f15
4
+ data.tar.gz: 6c87f09dfffd75f143b3fa8f49d0c66e670558d62af49f240b5a1a210560d2d1
5
5
  SHA512:
6
- metadata.gz: 7dba889d195cd4e4f6cf6edc640f6e2b3090822e272961aa0041bd7fa1e30ac75d2a624b8ca7433aa43d4bc16401416664c0666bfcfcf29e7a1a0128555e76a9
7
- data.tar.gz: e123cb980d054dd4c853143f22c35423712891feb1a8bf69a1f33af8f9fa0c85ac47b2195542a09a3077db8926843b980f77ccc3d05b4185a312ee5aa00ae971
6
+ metadata.gz: e9c8b7dc58977b80469b01811b525a0402b381baf54337806eb00c05ef2b9f90b3e9078a521b221f234113387f52524b01862129c22c93871b33cf89fe4286e0
7
+ data.tar.gz: 241fa758312ee4570da8732246c450e161c27dc0c1ca7344a99d8832d7a34058ed601037133389d0033ecac31218960dc22846b3fccb426d3883f2b22bba7db9
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ # [0.5.2] - 2026-02-23
2
+
3
+ - Fix bug that results in duplicate required entries when merging declarations
4
+ - Communicate which nested attributes are guaranteed to exist to help external generators
5
+
1
6
  # [0.5.1] - 2026-01-29
2
7
 
3
8
  - Fix bug where checking defaults on a type reference explodes
@@ -1850,12 +1850,12 @@ RSpec.describe Foobara::CommandConnector do
1850
1850
  expect(JSON.parse(response.body)).to eq(
1851
1851
  "stuff2" => {
1852
1852
  "things2" => [
1853
- "user" => {
1853
+ { "user" => {
1854
1854
  "id" => user_id,
1855
1855
  "name" => "whatever",
1856
1856
  "foo" => "bar",
1857
1857
  "username" => "some_username"
1858
- }
1858
+ } }
1859
1859
  ]
1860
1860
  }
1861
1861
  )
@@ -0,0 +1,6 @@
1
+ guard :rspec, all_after_pass: false, all_on_start: true, cmd: "bundle exec rspec", failed_mode: :focus do
2
+ watch(%r{^spec/(.+)_spec\.rb$})
3
+ watch(%r{^src/(.+)\.rb$}) { "spec/" }
4
+ watch(%r{^lib/(.+)\.rb$}) { "spec/" }
5
+ watch(%r{^spec/spec_helper.rb$}) { "spec/" }
6
+ end
@@ -23,10 +23,57 @@ module Foobara
23
23
  model_name: foobara_model_name,
24
24
  model_base_class: foobara_type.declaration_data[:model_base_class],
25
25
  model_class: foobara_type.declaration_data[:model_class],
26
- delegates: foobara_delegates,
26
+ delegates: foobara_delegates_manifest,
27
27
  private: foobara_private_attribute_names
28
28
  )
29
29
  end
30
+
31
+ private
32
+
33
+ def foobara_delegates_manifest
34
+ if foobara_delegates&.any?
35
+ manifest = foobara_delegates.dup
36
+ guaranteed_to_exist = []
37
+
38
+ delegates.each_pair do |attribute_name, delegate_manifest|
39
+ data_path = DataPath.for(delegate_manifest[:data_path])
40
+
41
+ if delegate_guaranteed_to_exist?(data_path)
42
+ guaranteed_to_exist << attribute_name
43
+ end
44
+ end
45
+
46
+ if guaranteed_to_exist.any?
47
+ manifest[:guaranteed_to_exist] = guaranteed_to_exist
48
+ end
49
+
50
+ manifest
51
+ end
52
+ end
53
+
54
+ def delegate_guaranteed_to_exist?(data_path)
55
+ parent_path = data_path.parent
56
+ return true unless parent_path
57
+
58
+ return false unless delegate_guaranteed_to_exist?(parent_path)
59
+
60
+ parent_type = model_type.type_at_path(parent_path)
61
+
62
+ parent_type = if parent_type.extends?(BuiltinTypes[:model])
63
+ parent_type.target_class.attributes_type
64
+ elsif parent_type.extends?(BuiltinTypes[:attributes])
65
+ parent_type
66
+ end
67
+
68
+ return true unless parent_type
69
+
70
+ symbol = data_path.last
71
+
72
+ parent_declaration_data = parent_type.declaration_data
73
+
74
+ parent_declaration_data[:required]&.include?(symbol) ||
75
+ parent_declaration_data[:defaults]&.key?(symbol)
76
+ end
30
77
  end
31
78
  end
32
79
  end
@@ -49,7 +49,8 @@ RSpec.describe Foobara::Model do
49
49
  expect(manifest[:delegates]).to eq(
50
50
  username: {
51
51
  data_path: "auth_user.username"
52
- }
52
+ },
53
+ guaranteed_to_exist: [:username]
53
54
  )
54
55
 
55
56
  expect(manifest[:declaration_data][:delegates]).to eq(
@@ -77,7 +78,8 @@ RSpec.describe Foobara::Model do
77
78
  username: {
78
79
  data_path: "auth_user.username",
79
80
  writer: true
80
- }
81
+ },
82
+ guaranteed_to_exist: [:username]
81
83
  )
82
84
 
83
85
  expect(manifest[:declaration_data][:delegates]).to eq(
@@ -150,7 +152,8 @@ RSpec.describe Foobara::Model do
150
152
  expect(manifest[:delegates]).to eq(
151
153
  username: {
152
154
  data_path: "auth_user.username"
153
- }
155
+ },
156
+ guaranteed_to_exist: [:username]
154
157
  )
155
158
 
156
159
  expect(manifest[:declaration_data][:delegates]).to eq(
@@ -206,7 +209,8 @@ RSpec.describe Foobara::Model do
206
209
  username: {
207
210
  data_path: "auth_user.username",
208
211
  writer: true
209
- }
212
+ },
213
+ guaranteed_to_exist: [:username]
210
214
  )
211
215
 
212
216
  expect(manifest[:declaration_data][:delegates]).to eq(
@@ -0,0 +1,6 @@
1
+ guard :rspec, all_after_pass: false, all_on_start: true, cmd: "bundle exec rspec", failed_mode: :focus do
2
+ watch(%r{^spec/(.+)_spec\.rb$})
3
+ watch(%r{^src/(.+)\.rb$}) { "spec/" }
4
+ watch(%r{^lib/(.+)\.rb$}) { "spec/" }
5
+ watch(%r{^spec/spec_helper.rb$}) { "spec/" }
6
+ end
@@ -71,4 +71,56 @@ RSpec.describe Foobara::Manifest do
71
71
  it { is_expected.to be(true) }
72
72
  end
73
73
  end
74
+
75
+ describe "#guaranteed_to_exist?" do
76
+ subject { model.guaranteed_to_exist?(attribute_name) }
77
+
78
+ let(:attribute_name) { :name }
79
+
80
+ context "when the model has no delegates" do
81
+ let(:some_model) do
82
+ stub_class "SomeModel", Foobara::Model do
83
+ attributes do
84
+ name :string
85
+ end
86
+ end
87
+ end
88
+
89
+ it { is_expected.to be(false) }
90
+ end
91
+
92
+ context "when it's nested attributes" do
93
+ let(:some_model) do
94
+ stub_class "SomeModel", Foobara::Model do
95
+ attributes do
96
+ name do
97
+ first :string
98
+ last :string
99
+ end
100
+ end
101
+
102
+ delegate_attribute :first, :name
103
+ end
104
+ end
105
+
106
+ it { is_expected.to be(false) }
107
+ end
108
+
109
+ context "when required all the way down" do
110
+ let(:some_model) do
111
+ stub_class "SomeModel", Foobara::Model do
112
+ attributes do
113
+ name_parts :required do
114
+ name :string, :required
115
+ surname :string
116
+ end
117
+ end
118
+
119
+ delegate_attribute :name, :name_parts
120
+ end
121
+ end
122
+
123
+ it { is_expected.to be(true) }
124
+ end
125
+ end
74
126
  end
@@ -5,12 +5,24 @@ module Foobara
5
5
  class Model < Type
6
6
  self.category_symbol = :type
7
7
 
8
+ optional_keys :delegates
9
+
8
10
  alias model_manifest relevant_manifest
9
11
 
10
12
  def attributes_type
11
13
  Attributes.new(root_manifest, [*manifest_path, :declaration_data, :attributes_declaration])
12
14
  end
13
15
 
16
+ def guaranteed_to_exist?(attribute_name)
17
+ return true if attributes_type.required?(attribute_name)
18
+
19
+ guaranteed_to_exist = DataPath.value_at([:delegates, :guaranteed_to_exist], relevant_manifest)
20
+
21
+ return false unless guaranteed_to_exist
22
+
23
+ guaranteed_to_exist.include?(attribute_name.to_sym) || guaranteed_to_exist.include?(attribute_name.to_s)
24
+ end
25
+
14
26
  def attribute_names
15
27
  attributes_type.attribute_names
16
28
  end
@@ -221,6 +221,12 @@ module Foobara
221
221
  path.empty?
222
222
  end
223
223
 
224
+ def parent
225
+ if path.size > 1
226
+ DataPath.new(path[0..-2])
227
+ end
228
+ end
229
+
224
230
  private
225
231
 
226
232
  def normalize_all(key_parts)
@@ -17,7 +17,7 @@ module Foobara
17
17
  end
18
18
 
19
19
  if type_required && !type_required.empty?
20
- required += type_required
20
+ required |= type_required
21
21
  end
22
22
  end
23
23
 
data/version.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Foobara
2
2
  module Version
3
- VERSION = "0.5.1".freeze
3
+ VERSION = "0.5.2".freeze
4
4
  MINIMUM_RUBY_VERSION = ">= 3.4.0".freeze
5
5
  end
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foobara
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miles Georgi
@@ -186,6 +186,7 @@ files:
186
186
  - projects/domain_mapper/lib/foobara/domain_mapper.rb
187
187
  - projects/domain_mapper/src/domain_mapper.rb
188
188
  - projects/domain_mapper/src/domain_mapper_lookups.rb
189
+ - projects/entities/Guardfile
189
190
  - projects/entities/projects/detached_entity/lib/foobara/detached_entity.rb
190
191
  - projects/entities/projects/detached_entity/src/concerns/aliases.rb
191
192
  - projects/entities/projects/detached_entity/src/concerns/associations.rb
@@ -335,6 +336,7 @@ files:
335
336
  - projects/entities_plumbing/spec/support/term_trap.rb
336
337
  - projects/entities_plumbing/src/command_connectors_extension.rb
337
338
  - projects/entities_plumbing/src/extensions/authenticator.rb
339
+ - projects/manifest/Guardfile
338
340
  - projects/manifest/lib/foobara/manifest.rb
339
341
  - projects/manifest/spec/manifest_spec.rb
340
342
  - projects/manifest/spec/model_spec.rb