purple-client 0.1.7.2 → 0.1.7.4

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: 69afc0be89db74d4a3b76af2c93c31652ba2443491f5e9c20303e263cb3075d5
4
- data.tar.gz: de1de5487aeca7098cda9d761f026b988c3d2ee7b77d2bd0c5737467ca80bb47
3
+ metadata.gz: 81f0cb64364ec0312d07b900ae67a2e65137d03818c6b3e2c1b8631221ea9a65
4
+ data.tar.gz: 7404f01d3ba5714093d62fbac16218b263f20c43d0b29391add9fd9833ccdb29
5
5
  SHA512:
6
- metadata.gz: 66fd354ebdab532ca2a35b3972eb2a7d6a8d49d1fd764b0f61e3629d9032338ca117de2240c73d26e140a6e0b31e96223cbf6d701d56ec71b1eab5f0b0e2030b
7
- data.tar.gz: 79fe2ed7920d8115ffb53bde9a2619604df8e9a981e70b83096839b2fd898381747d5f42736644e87d5f63d36c7377dd4110b9332d486900c06a9dd94a8d16e8
6
+ metadata.gz: ab18072bf25f80e0ded0dc7d3f317230fb93e400cbc9a563bf0d6be68b3c166458bf94f39877eb6dc535bce4c1e8c962b002699e3f4e08702effee65ba86e397
7
+ data.tar.gz: ef5033eb7da7a10bf033583603906b31176a63222e95e5f18bbdcab613b4ac775d4cb76594419c796206d65680dfc836d88ffe8f62973b38ad867a81928a1c8d
data/README.md CHANGED
@@ -121,6 +121,36 @@ end
121
121
  PostsClient.user_posts(user_id: 7)
122
122
  ```
123
123
 
124
+ ### Paths nested under parameters
125
+
126
+ When a path segment is marked with `is_param: true`, any paths nested
127
+ inside it will not have a `root_method` generated. Instead of calling a
128
+ root method, you need to chain the segment methods manually.
129
+
130
+ ```ruby
131
+ class BrowserClient < Purple::Client
132
+ domain 'https://api.example.com'
133
+
134
+ path :browser do
135
+ path :id, is_param: true do
136
+ path :web, method: :post do
137
+ response :ok do
138
+ end
139
+
140
+ response :bad_request do
141
+ body do |res|
142
+ puts res
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+
150
+ # root_method :web will not work here
151
+ BrowserClient.browser.id('123').web
152
+ ```
153
+
124
154
  ### Callbacks with additional arguments
125
155
 
126
156
  ```ruby
@@ -174,6 +204,119 @@ class AccountsClient < Purple::Client
174
204
  end
175
205
  ```
176
206
 
207
+ ### Optional fields
208
+
209
+ Sometimes an API response omits certain keys. You can mark those fields as
210
+ optional in the body definition so their absence doesn't raise validation errors.
211
+
212
+ ```ruby
213
+ class CalendarClient < Purple::Client
214
+ domain 'https://api.example.com'
215
+
216
+ path :schedule do
217
+ response :ok do
218
+ body(
219
+ day: { type: Integer, optional: true },
220
+ )
221
+ end
222
+ root_method :schedule
223
+ end
224
+ end
225
+
226
+ # The `day` attribute may be missing in the response
227
+ CalendarClient.schedule
228
+ ```
229
+
230
+ ### Allow blank fields
231
+
232
+ Some APIs return keys that are present but contain `null` or empty string values.
233
+ You can mark those fields with `allow_blank` so blank values do not raise
234
+ validation errors.
235
+
236
+ ```ruby
237
+ class ProfilesClient < Purple::Client
238
+ domain 'https://api.example.com'
239
+
240
+ path :profile do
241
+ response :ok do
242
+ body(
243
+ middle_name: { type: String, allow_blank: true },
244
+ )
245
+ end
246
+ root_method :profile
247
+ end
248
+ end
249
+
250
+ # The `middle_name` attribute may be blank or omitted in the response
251
+ ProfilesClient.profile
252
+ ```
253
+
254
+ ### Array responses
255
+
256
+ When an endpoint returns an array of objects, you can use `:array_of` to
257
+ describe the structure of each element in the array.
258
+
259
+ ```ruby
260
+ class MerchantsClient < Purple::Client
261
+ domain 'https://api.example.com'
262
+
263
+ path :merchants do
264
+ response :ok do
265
+ structure = {
266
+ id: Integer,
267
+ name: String,
268
+ address: String,
269
+ work_time: String,
270
+ accepts_qr: { type: String, optional: true }
271
+ }
272
+
273
+ body(:array_of, **structure)
274
+ end
275
+ root_method :merchants
276
+ end
277
+ end
278
+
279
+ # Each array element will be validated against the structure
280
+ MerchantsClient.merchants
281
+ ```
282
+
283
+ ### Response body processing
284
+
285
+ After the body structure is validated, you can supply a block to `body`
286
+ to transform or handle the parsed response. This is useful for mapping
287
+ error payloads to simpler return values or for normalizing data.
288
+
289
+ ```ruby
290
+ class MessagesClient < Purple::Client
291
+ domain 'https://api.example.com'
292
+
293
+ path :messages do
294
+ response :unprocessable_entity do
295
+ structure = {
296
+ status: Integer,
297
+ type: String,
298
+ title: String,
299
+ detail: String
300
+ }
301
+
302
+ body(**structure) do |res|
303
+ case res.type
304
+ when 'errors/invalid_recipient'
305
+ :not_found
306
+ else
307
+ res
308
+ end
309
+ end
310
+ end
311
+ root_method :send_message
312
+ end
313
+ end
314
+
315
+ # Returns :not_found when the recipient is invalid, otherwise returns the
316
+ # parsed response body.
317
+ MessagesClient.send_message
318
+ ```
319
+
177
320
  ## Development
178
321
 
179
322
  After checking out the repo, run `bin/setup` to install dependencies. Then run
@@ -0,0 +1,7 @@
1
+ require_relative '../version'
2
+
3
+ module Purple
4
+ class Client
5
+ VERSION = Purple::VERSION
6
+ end
7
+ end
data/lib/purple/client.rb CHANGED
@@ -6,7 +6,8 @@ require 'purple/requests/authorization'
6
6
  require 'purple/response'
7
7
  require 'purple/responses/body'
8
8
  require 'purple/boolean'
9
- require_relative "version"
9
+ require_relative 'version'
10
+ require_relative 'client/version'
10
11
 
11
12
  module Purple
12
13
  class Client
data/lib/purple/path.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'dry-initializer'
4
+ require 'faraday'
5
+ require 'active_support/core_ext/hash/deep_merge'
6
+ require 'active_support/core_ext/object/inclusion'
4
7
 
5
8
  module Purple
6
9
  class Path
@@ -24,7 +27,7 @@ module Purple
24
27
  @param_value = args.first
25
28
  end
26
29
 
27
- def method_missing(method_name, *args, &)
30
+ def method_missing(method_name, *args, **kw_args, &)
28
31
  if children.any? { |child| child.name == method_name }
29
32
  child = children.find { |child| child.name == method_name }
30
33
 
@@ -35,7 +38,7 @@ module Purple
35
38
  if child.children.any?
36
39
  child
37
40
  else
38
- callback_arguments = additional_callback_arguments.map do |arg|
41
+ callback_arguments = client.additional_callback_arguments.map do |arg|
39
42
  kw_args.delete(arg)
40
43
  end
41
44
 
@@ -12,6 +12,7 @@ class Purple::Response
12
12
  CODES = {
13
13
  ok: 200,
14
14
  created: 201,
15
+ accepted: 202,
15
16
  bad_request: 400,
16
17
  unauthorized: 401,
17
18
  unprocessable_entity: 422,
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'purple/responses'
4
4
  require 'purple/responses/object'
5
+ require "active_support/core_ext/string/inflections"
5
6
 
6
7
  class Purple::Responses::Body
7
8
  extend Dry::Initializer[undefined: false]
@@ -75,6 +76,8 @@ class Purple::Responses::Body
75
76
 
76
77
  check_type!(object, key, value[:type])
77
78
  else
79
+ next if object[key].nil?
80
+
78
81
  check_structure!(object[key], substructure[key])
79
82
  end
80
83
  elsif value.is_a?(Array)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Purple returns objects as responses from API. This is a base class for all responses objects.
4
+ require 'active_support/core_ext/module/delegation'
4
5
  class Purple::Responses::Object
5
6
  attr_accessor :attributes
6
7
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Purple
4
- VERSION = "0.1.7.2"
4
+ VERSION = "0.1.7.4"
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.2
4
+ version: 0.1.7.4
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-07-08 00:00:00.000000000 Z
11
+ date: 2025-08-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-initializer
@@ -24,6 +24,34 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: faraday
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
27
55
  description: Build API wrappers faster
28
56
  email:
29
57
  - kalashnikovisme@gmail.com
@@ -40,6 +68,7 @@ files:
40
68
  - Rakefile
41
69
  - lib/purple/boolean.rb
42
70
  - lib/purple/client.rb
71
+ - lib/purple/client/version.rb
43
72
  - lib/purple/path.rb
44
73
  - lib/purple/request.rb
45
74
  - lib/purple/requests/authorization.rb