smart_params 2.3.1 → 2.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2378ae8b0fa20a7f05f0895eb820bf1f48c93c578f2f673ee4a0876f7b3abc6b
4
- data.tar.gz: 5904cd2c17c7f1ae03d14f28389acdd5d9902ffbe31c6669e73ec64a10e296c1
3
+ metadata.gz: dee376c52b0335bfc7ae84b37b2fc1e10575b216ad11e131fbf858621fa7431f
4
+ data.tar.gz: cfa443e6596f128bc313a83d8e744ca586c52891eecab9011edb330cbf365d40
5
5
  SHA512:
6
- metadata.gz: 7e77d8dc227e0d2460282aca3f1fc15286d49032b90feca3d2107172d0115d164b694d22d8af9cd4835d469ac9341e471025f9cf9e5421ab281b8b749725dc02
7
- data.tar.gz: 0a93159bd0d057b1d9af2218a74c7112e47b9034b7fe22e0711184bde7c1eb4463e5d74af40d924668c9efa8f0693461953478c8ed30ebea5683aa7351ce9269
6
+ metadata.gz: b2ab41e38e0ad3411de6703a3f37e3c1f1fa3cdef75de1404729d329727883c269da29119667bcdf5ceb9f7528e60802b708567e1f2dacc72eed8dd99824f7b7
7
+ data.tar.gz: 0213e4c1bb2854ba43fd6ac7c8300726e882e486d94c9032f7964929239854fb9780f84dc81c7f45200ebc2e0113afb64a856cd6711aee9544f6d275d71603c0
data/README.md CHANGED
@@ -148,6 +148,7 @@ Or install it yourself with:
148
148
  1. Read the [Code of Conduct](/CONDUCT.md)
149
149
  2. Fork it
150
150
  3. Create your feature branch (`git checkout -b my-new-feature`)
151
- 4. Commit your changes (`git commit -am 'Add some feature'`)
152
- 5. Push to the branch (`git push origin my-new-feature`)
153
- 6. Create new Pull Request
151
+ 4. Test your code: `rake spec`
152
+ 5. Commit your changes (`git commit -am 'Add some feature'`)
153
+ 6. Push to the branch (`git push origin my-new-feature`)
154
+ 7. Create new Pull Request
data/lib/smart_params.rb CHANGED
@@ -6,7 +6,7 @@ require "active_support/core_ext/module/delegation"
6
6
 
7
7
  module SmartParams
8
8
  extend ActiveSupport::Concern
9
- include Dry::Types.module
9
+ include Dry.Types()
10
10
 
11
11
  require_relative "smart_params/field"
12
12
  require_relative "smart_params/error"
@@ -68,12 +68,8 @@ module SmartParams
68
68
  # This function basically takes a list of fields and reduces them into a tree of values
69
69
  private def structure
70
70
  fields
71
- .reject(&:empty?)
71
+ .reject(&:removable?)
72
72
  .map(&:to_hash)
73
- .map do |hash|
74
- # NOTE: okay, so this looks weird, but it's because the root type has no key
75
- if hash.key?(nil) then hash.fetch(nil) else hash end
76
- end
77
73
  .reduce(&:deep_merge)
78
74
  end
79
75
 
@@ -1,14 +1,16 @@
1
1
  module SmartParams
2
2
  class Field
3
3
  attr_reader :keychain
4
- attr_reader :value
5
4
  attr_reader :subfields
6
5
  attr_reader :type
7
6
 
8
- def initialize(keychain:, type:, &nesting)
7
+ def initialize(keychain:, type:, nullable: false, &nesting)
9
8
  @keychain = Array(keychain)
10
9
  @subfields = Set.new
11
10
  @type = type
11
+ @nullable = nullable
12
+ @specified = false
13
+ @dirty = false
12
14
 
13
15
  if block_given?
14
16
  instance_eval(&nesting)
@@ -16,9 +18,52 @@ module SmartParams
16
18
  end
17
19
 
18
20
  def deep?
21
+ # We check @specified directly because we want to know if ANY
22
+ # subfields have been passed, not just ones that match the schema.
23
+ return false if nullable? && !!@specified
19
24
  subfields.present?
20
25
  end
21
26
 
27
+ def root?
28
+ keychain.size == 0
29
+ end
30
+
31
+ def value
32
+ @value || ({} if root?)
33
+ end
34
+
35
+ def nullable?
36
+ !!@nullable
37
+ end
38
+
39
+ def specified?
40
+ if nullable?
41
+ !!@specified && clean?
42
+ else
43
+ !!@specified
44
+ end
45
+ end
46
+
47
+ # For nullable hashes: Any keys not in the schema make the hash dirty.
48
+ # If a key is found that matches the schema, we can consider the hash
49
+ # clean.
50
+ def dirty?
51
+ !!@dirty
52
+ end
53
+
54
+ def clean?
55
+ return false if dirty?
56
+ return true if empty? || subfields.select { |sub| !sub.empty? }.any?
57
+ false
58
+ end
59
+
60
+ # Check if we should consider this value even when empty.
61
+ def allow_empty?
62
+ return true if specified? && nullable?
63
+ return subfields.select(&:allow_empty?).any?
64
+ false
65
+ end
66
+
22
67
  def claim(raw)
23
68
  return type[dug(raw)] if deep?
24
69
 
@@ -37,20 +82,48 @@ module SmartParams
37
82
  value.nil?
38
83
  end
39
84
 
85
+ # Should this field be removed from resulting hash?
86
+ def removable?
87
+ empty? && !allow_empty?
88
+ end
89
+
40
90
  def weight
41
91
  keychain.map(&:to_s)
42
92
  end
43
93
 
44
- private def field(key, type:, &subfield)
45
- @subfields << self.class.new(keychain: [*keychain, key], type: type, &subfield)
94
+ private def field(key, type:, nullable: false, &subfield)
95
+ @subfields << self.class.new(keychain: [*keychain, key], type: type, nullable: nullable, &subfield)
46
96
  end
47
97
 
98
+ # Very busy method with recent changes. TODO: clean-up
48
99
  private def dug(raw)
49
- if keychain.empty?
50
- raw
51
- else
52
- raw.dig(*keychain)
100
+ return raw if keychain.empty?
101
+
102
+ # If value provided is a hash, check if it's dirty. See #dirty? for
103
+ # more info.
104
+ if nullable?
105
+ hash = raw.dig(*keychain)
106
+ if hash.respond_to?(:keys)
107
+ others = hash.keys - [keychain.last]
108
+ @dirty = others.any?
109
+ end
53
110
  end
111
+
112
+ # Trace the keychain to find out if the field is explicitly set in the
113
+ # input hash.
114
+ at = raw
115
+ exact = true
116
+ keychain.each { |key|
117
+ if at.respond_to?(:key?) && at.key?(key)
118
+ at = at[key]
119
+ else
120
+ exact = false
121
+ break
122
+ end
123
+ }
124
+ @specified = exact
125
+
126
+ raw.dig(*keychain)
54
127
  end
55
128
  end
56
129
  end
@@ -1,3 +1,3 @@
1
1
  module SmartParams
2
- VERSION = "2.3.1"
2
+ VERSION = "2.4.0"
3
3
  end
@@ -2,6 +2,8 @@ require "spec_helper"
2
2
 
3
3
  RSpec.describe SmartParams do
4
4
  let(:schema) { CreateAccountSchema.new(params) }
5
+ let(:nullable_schema) { NullableSchema.new(params) }
6
+ let(:nullable_required_subfield_schema) { NullableRequiredSubfieldSchema.new(params) }
5
7
 
6
8
  describe ".new" do
7
9
  context "with an empty params" do
@@ -42,7 +44,8 @@ RSpec.describe SmartParams do
42
44
  data: {
43
45
  type: "accounts",
44
46
  attributes: {
45
- email: "kurtis@example.com"
47
+ email: "kurtis@example.com",
48
+ password: "secret"
46
49
  }
47
50
  },
48
51
  meta: {
@@ -155,7 +158,8 @@ RSpec.describe SmartParams do
155
158
  data: {
156
159
  type: "accounts",
157
160
  attributes: {
158
- email: "kurtis@example.com"
161
+ email: "kurtis@example.com",
162
+ password: "secret"
159
163
  }
160
164
  },
161
165
  meta: {
@@ -234,7 +238,8 @@ RSpec.describe SmartParams do
234
238
  data: {
235
239
  type: "accounts",
236
240
  attributes: {
237
- email: "kurtis@example.com"
241
+ email: "kurtis@example.com",
242
+ password: "secret"
238
243
  }
239
244
  },
240
245
  meta: {
@@ -322,7 +327,8 @@ RSpec.describe SmartParams do
322
327
  data: {
323
328
  type: "accounts",
324
329
  attributes: {
325
- email: "kurtis@example.com"
330
+ email: "kurtis@example.com",
331
+ password: "secret"
326
332
  }
327
333
  },
328
334
  meta: {
@@ -368,4 +374,158 @@ RSpec.describe SmartParams do
368
374
  end
369
375
  end
370
376
  end
377
+
378
+ describe "nullable values" do
379
+ context "set to nil" do
380
+ subject {nullable_schema.to_hash}
381
+
382
+ let(:params) do
383
+ {
384
+ data: nil
385
+ }
386
+ end
387
+
388
+ it "returns explicit nil" do
389
+ expect(
390
+ subject
391
+ ).to match(
392
+ hash_including(
393
+ {
394
+ "data" => nil
395
+ }
396
+ )
397
+ )
398
+ end
399
+ end
400
+
401
+ context "provided matching data" do
402
+ subject {nullable_schema.to_hash}
403
+
404
+ let(:params) do
405
+ {
406
+ data: {
407
+ id: "1",
408
+ type: "people"
409
+ }
410
+ }
411
+ end
412
+
413
+ it "returns matching data" do
414
+ expect(
415
+ subject
416
+ ).to match(
417
+ hash_including(
418
+ {
419
+ "data" => hash_including(
420
+ {
421
+ "id" => "1",
422
+ "type" => "people"
423
+ }
424
+ )
425
+ }
426
+ )
427
+ )
428
+ end
429
+ end
430
+
431
+ context "not provided" do
432
+ subject {nullable_schema.to_hash}
433
+
434
+ let(:params) do
435
+ {
436
+ }
437
+ end
438
+
439
+ it "does not set nil relationship" do
440
+ expect(
441
+ subject
442
+ ).to match(
443
+ hash_excluding(
444
+ {
445
+ "data" => nil
446
+ }
447
+ )
448
+ )
449
+ end
450
+ end
451
+
452
+ context "with non matching subfield data" do
453
+ subject {nullable_schema.to_hash}
454
+
455
+ let(:params) do
456
+ {
457
+ data: {
458
+ is: "garbage"
459
+ }
460
+ }
461
+ end
462
+
463
+ it "does not provide data" do
464
+ expect(
465
+ subject
466
+ ).to match(
467
+ hash_excluding(
468
+ {
469
+ "data" => nil
470
+ }
471
+ )
472
+ )
473
+ end
474
+ end
475
+
476
+ context "specified with unclean data" do
477
+ subject {nullable_required_subfield_schema.to_hash}
478
+
479
+ let(:params) do
480
+ {
481
+ # This will raise an exception becase the data hash is specified
482
+ # but its required subfields are not.
483
+ data: {
484
+ is: 'garbage'
485
+ }
486
+ }
487
+ end
488
+
489
+ it "checks subfields" do
490
+ expect {
491
+ subject
492
+ }.to raise_exception(SmartParams::Error::InvalidPropertyType)
493
+ end
494
+ end
495
+
496
+ context "specified as null" do
497
+ subject {nullable_required_subfield_schema.to_hash}
498
+
499
+ let(:params) do
500
+ {
501
+ # This will not raise an error, since data is allowed to be null.
502
+ # Subfields will not be checked.
503
+ data: nil
504
+ }
505
+ end
506
+
507
+ it "checks subfields" do
508
+ expect {
509
+ subject
510
+ }.not_to raise_exception
511
+ end
512
+ end
513
+
514
+ context "unspecified with required subfield" do
515
+ subject {nullable_required_subfield_schema.to_hash}
516
+
517
+ let(:params) do
518
+ {
519
+ # In this case, the nullable data hash is not specified so we
520
+ # don't need to enforce constraints on subfields.
521
+ }
522
+ end
523
+
524
+ it "allows null value" do
525
+ expect {
526
+ subject
527
+ }.not_to raise_exception
528
+ end
529
+ end
530
+ end
371
531
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_params
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.1
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kurtis Rainbolt-Greene
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-08-12 00:00:00.000000000 Z
11
+ date: 2019-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -176,8 +176,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
176
176
  - !ruby/object:Gem::Version
177
177
  version: '0'
178
178
  requirements: []
179
- rubyforge_project:
180
- rubygems_version: 2.7.3
179
+ rubygems_version: 3.0.3
181
180
  signing_key:
182
181
  specification_version: 4
183
182
  summary: Apply an organized and easy to maintain schema to request params