smart_params 2.3.1 → 2.4.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: 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