purple-client 0.1.7.6 → 0.1.8

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: 2f57285cc0997c1352f58c090079dc236e52ecf08c040b1a2d68b2e44824b344
4
- data.tar.gz: '094e99048e10af59faabec36d819ec1b075a65bfb47eaebd422ddee9dd216d84'
3
+ metadata.gz: 904a3aae97dec86b01cba57ebe806b9745cbce75803e986e63ae953209642455
4
+ data.tar.gz: 92397c8c6a2862fd4366d58dd67bc255933252d91becad2af58241ed1a0e3234
5
5
  SHA512:
6
- metadata.gz: e2a8c339a3dfa90dc44444b421b13c819152e3f0c9ce3a0bede32604c9b5bf00ea9068bfff074bc84c8165093167f2c1bb8756ba0fbb059a0e8cb03ebc63705c
7
- data.tar.gz: 8ae3c368e5130486f5c4d466d9232095da167f957134ef2192798fc446b79f69ceb635c8bd1a14f91b745a8f0cdf6bab7384d9e71b46e9bdd17c30868da8bd5a
6
+ metadata.gz: 7072c78039ce88e59465cd91950eca347c11e876639490235f354ff5e8fad4cef31a84128c4b90ab28b6efd107bea3b91620f27f03c1ad19f06744803268510f
7
+ data.tar.gz: 6fa661c3de144a83de31fc3cdcdbd199ea6fd3d38643d2b60bbb4e243893074adaa19b6bc5daf7118b1a5339bb754d9fc3d1c809e09af7006281cb465177b7ae
data/lib/purple/path.rb CHANGED
@@ -4,6 +4,7 @@ require 'dry-initializer'
4
4
  require 'faraday'
5
5
  require 'active_support/core_ext/hash/deep_merge'
6
6
  require 'active_support/core_ext/object/inclusion'
7
+ require 'active_support/core_ext/object/blank'
7
8
 
8
9
  module Purple
9
10
  class Path
@@ -66,6 +67,10 @@ module Purple
66
67
  conn.headers = headers
67
68
  end
68
69
 
70
+ if client.domain.blank?
71
+ raise ArgumentError, 'Client domain is not set. Please set the domain in the client configuration.'
72
+ end
73
+
69
74
  unless client.domain.start_with?('http')
70
75
  raise ArgumentError, "Invalid URL: #{client.domain}. Ensure you have set protocol (http/https) in the client domain."
71
76
  end
@@ -20,29 +20,29 @@ class Purple::Responses::Body
20
20
  def validate!(body, arguments)
21
21
  parsed_body = JSON.parse(body, symbolize_names: true)
22
22
 
23
- result = if parsed_body.is_a? Integer
24
- parsed_body
25
- else
26
- underscored_body = if parsed_body.is_a? Array
27
- parsed_body.map { |item| item.transform_keys { |key| key.to_s.underscore.to_sym } }
28
- else
29
- parsed_body.transform_keys { |key| key.to_s.underscore.to_sym }
30
- end
31
-
32
- if underscored_body.is_a? Array
33
- underscored_body.each do |item|
34
- check_structure!(item)
35
- end
36
- else
37
- check_structure!(underscored_body)
38
- end
39
-
40
- if underscored_body.is_a?(Array)
41
- underscored_body.map { |item| create_object(item) }
42
- else
43
- create_object(underscored_body)
44
- end
45
- end
23
+ if parsed_body.is_a? Integer
24
+ parsed_body
25
+ else
26
+ underscored_body = if parsed_body.is_a? Array
27
+ parsed_body.map { |item| item.transform_keys { |key| key.to_s.underscore.to_sym } }
28
+ else
29
+ parsed_body.transform_keys { |key| key.to_s.underscore.to_sym }
30
+ end
31
+
32
+ if underscored_body.is_a? Array
33
+ underscored_body.each do |item|
34
+ check_structure!(item)
35
+ end
36
+ else
37
+ check_structure!(underscored_body)
38
+ end
39
+
40
+ if underscored_body.is_a?(Array)
41
+ underscored_body.map { |item| create_klass(item, structure) }
42
+ else
43
+ create_klass(underscored_body, structure)
44
+ end
45
+ end => result
46
46
 
47
47
  if transform.is_a?(Proc)
48
48
  transform.call(result, arguments)
@@ -55,16 +55,24 @@ class Purple::Responses::Body
55
55
 
56
56
  private
57
57
 
58
- def create_object(body)
59
- object = Class.new(Purple::Responses::Object) do
58
+ def create_klass(body, structure)
59
+ klass = Class.new(Purple::Responses::Object) do
60
60
  body.each do |key, value|
61
61
  define_method(key) { value }
62
62
  end
63
+
64
+ structure.each do |key, value|
65
+ if value.is_a?(Hash) && value[:optional] == true && !body.key?(key)
66
+ define_method(key) do
67
+ raise NoMethodError, "Optional field '#{key}' is not present in the response body. Use `contain?(:#{key})` to check its presence."
68
+ end
69
+ end
70
+ end
63
71
  end.new
64
72
 
65
- object.attributes = body
73
+ klass.attributes = body
66
74
 
67
- object
75
+ klass
68
76
  end
69
77
 
70
78
  def check_structure!(object, substructure = structure)
@@ -81,12 +89,26 @@ class Purple::Responses::Body
81
89
  check_structure!(object[key], substructure[key])
82
90
  end
83
91
  elsif value.is_a?(Array)
84
- object[key].each do |item|
85
- if value[0].is_a?(Symbol)
86
- raise "Body structure definition error in key '#{key}' of structure #{substructure}."
87
- end
92
+ if object[key].nil?
93
+ raise BodyStructureMismatchError.new(key, value, nil, object),
94
+ "Expected a non-nil array for '#{key}' in response body.\n\nExpected response structure: #{substructure}.\n\nUse '#{key}: { type: #{value}, allow_blank: true }' if this field can be nil."
95
+ end
88
96
 
89
- check_structure!(item, value[0])
97
+ type = value.first
98
+
99
+ if type.is_a?(Symbol)
100
+ raise "Body structure definition error in key '#{key}' of structure #{substructure}."
101
+ end
102
+
103
+ object[key].each_with_index do |item, index|
104
+ if type.is_a?(Class)
105
+ unless item.is_a?(type)
106
+ raise BodyStructureMismatchError.new(key, type, item, object),
107
+ "Expected item at #{index} index of '#{key}' to be of type '#{value[index]}', but got '#{item.class}' with value '#{item}'."
108
+ end
109
+ else
110
+ check_structure!(item, type)
111
+ end
90
112
  end
91
113
  else
92
114
  if object.nil?
@@ -104,7 +126,7 @@ class Purple::Responses::Body
104
126
 
105
127
  unless object.key?(key)
106
128
  raise BodyStructureMismatchError.new(key, expected_type, object[key], object),
107
- "Missing field '#{key}' in response body. Body: #{object}"
129
+ "Missing field '#{key}' in response body. Body: #{object}\n\nUse '#{key}: { type: #{expected_type}, optional: true }' if this field may be absent."
108
130
  end
109
131
 
110
132
  return if expected_type == Purple::Boolean && (object[key] == true || object[key] == false)
@@ -6,4 +6,8 @@ class Purple::Responses::Object
6
6
  attr_accessor :attributes
7
7
 
8
8
  delegate :to_s, to: :attributes
9
+
10
+ def contain?(key)
11
+ attributes.key?(key)
12
+ end
9
13
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Purple
4
- VERSION = "0.1.7.6"
4
+ VERSION = "0.1.8"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: purple-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7.6
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pavel Kalashnikov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-09-01 00:00:00.000000000 Z
11
+ date: 2025-09-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-initializer