purple-client 0.1.2 → 0.1.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: 9d18a27dc35385412674a327de6cb947d1b4d8efe4e4032acc9edc1513ce24da
4
- data.tar.gz: 3611ed61c25f22b6f9f458894ca52bf611b10874051073485e9e67317529c3b5
3
+ metadata.gz: 426284067d37937cf20cc78132fc5286f4d691ae66cb25177f01c7219e844667
4
+ data.tar.gz: e08e9819a221659f94b9bbb54b41aa59a23ee1bc7fb2b8ca829e5a8039173325
5
5
  SHA512:
6
- metadata.gz: 5e5bc2479a7b5b259113e76ca91d1578a180afa3b792c12608bdb03d0c23b28e5ddfd1aa3ee3ecf96b32700fd140ed84dcb6330b19da3fd908ea97fa14600f2d
7
- data.tar.gz: 30e8ca1120a40557ffda8a51c531ee06c34807e3696ff424c056bf237819765b6f0171c115da9089234088e4a4ae9b32d366a67143149112a401c7862ea1bad0
6
+ metadata.gz: 0eb4508617a8198b6dd0cddfef087aabe63b65c4a8825bfe759caf379aa2a3adbb0b7705181363a937d2fa818b7d77458da4dcd244ba0da03ce2a0184d6df2cd
7
+ data.tar.gz: 22df3ed5def051f3a9fb372143a19e830276cd70a18756940ce1ec807b732273353788128c818fe5d3c303081b09d0904d9afd8bac281151e620d1a1e1ef4154
data/README.md CHANGED
@@ -1,43 +1,153 @@
1
1
  # Purple::Client
2
2
 
3
- TODO: Delete this and the text below, and describe your gem
4
-
5
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/purple/client`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ Purple::Client is a small DSL that helps you describe HTTP APIs. You define a domain, paths, and response structures, and the library generates handy methods for interacting with your service.
6
4
 
7
5
  ## Installation
8
6
 
9
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
10
-
11
- Install the gem and add to the application's Gemfile by executing:
7
+ Add the gem to your project:
12
8
 
13
9
  ```bash
14
- bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
10
+ bundle add purple-client
15
11
  ```
16
12
 
17
- If bundler is not being used to manage dependencies, install the gem by executing:
13
+ Or install it manually with:
18
14
 
19
15
  ```bash
20
- gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
16
+ gem install purple-client
21
17
  ```
22
18
 
23
19
  ## Usage
24
20
 
25
- TODO: Write usage instructions here
21
+ Below are some basic examples of how to define requests and call them. Each
22
+ snippet defines a custom class that inherits from `Purple::Client`.
23
+
24
+ ### Simple GET request
25
+
26
+ ```ruby
27
+ class StatusClient < Purple::Client
28
+ domain 'https://api.example.com'
29
+
30
+ path :status do
31
+ response :ok do
32
+ body :default
33
+ end
34
+ root_method :status
35
+ end
36
+ end
37
+
38
+ # Performs GET https://api.example.com/status
39
+ StatusClient.status
40
+ ```
41
+
42
+ ### Path with a dynamic parameter
43
+
44
+ ```ruby
45
+ class JobsClient < Purple::Client
46
+ domain 'https://api.example.com'
47
+
48
+ path :jobs do
49
+ path :job_id, is_param: true do
50
+ response :ok do
51
+ body id: Integer, name: String
52
+ end
53
+ root_method :job
54
+ end
55
+ end
56
+ end
57
+
58
+ # Performs GET https://api.example.com/jobs/123
59
+ JobsClient.job(123)
60
+ ```
61
+
62
+ ### Using authorization
63
+
64
+ ```ruby
65
+ class ProfileClient < Purple::Client
66
+ domain 'https://api.example.com'
67
+ authorization :bearer, 'TOKEN'
68
+
69
+ path :profile do
70
+ response :ok do
71
+ body :default
72
+ end
73
+ root_method :profile
74
+ end
75
+ end
76
+
77
+ # Authorization header will be sent automatically
78
+ ProfileClient.profile
79
+ ```
80
+
81
+ ### Nested paths
82
+
83
+ ```ruby
84
+ class PostsClient < Purple::Client
85
+ domain 'https://api.example.com'
86
+
87
+ path :users do
88
+ path :user_id, is_param: true do
89
+ path :posts do
90
+ response :ok do
91
+ body [{ id: Integer, title: String }]
92
+ end
93
+ root_method :user_posts
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ # Performs GET https://api.example.com/users/7/posts
100
+ PostsClient.user_posts(user_id: 7)
101
+ ```
102
+
103
+ ### Callbacks with additional arguments
104
+
105
+ ```ruby
106
+ class EventsClient < Purple::Client
107
+ domain 'https://api.example.com'
108
+
109
+ additional_callback_arguments :resource
110
+
111
+ callback do |url, params, headers, response, resource|
112
+ StoreEvent.call(url:, params:, headers:, response:, resource:)
113
+ end
114
+
115
+ path :events do
116
+ response :ok do
117
+ body :default
118
+ end
119
+ root_method :events
120
+ end
121
+ end
122
+
123
+ resource = SomeModel.find(1)
124
+ EventsClient.events(resource:)
125
+ ```
126
+
127
+ `additional_callback_arguments` lets you specify parameter names that will be
128
+ extracted from the call and passed to your callback. In the example above the
129
+ `resource` keyword argument is removed from the request parameters, but is
130
+ available inside the callback so you can associate the stored event with a
131
+ record of your choice.
26
132
 
27
133
  ## Development
28
134
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
135
+ After checking out the repo, run `bin/setup` to install dependencies. Then run
136
+ `rake spec` to execute the tests. You can also run `bin/console` for an interactive
137
+ prompt to experiment with the library.
30
138
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
139
+ To install this gem onto your local machine, run `bundle exec rake install`.
140
+ To release a new version, update the version number in `version.rb`, then run
141
+ `bundle exec rake release`.
32
142
 
33
143
  ## Contributing
34
144
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/purple-client. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/purple-client/blob/main/CODE_OF_CONDUCT.md).
145
+ Bug reports and pull requests are welcome on GitHub at
146
+ https://github.com/[USERNAME]/purple-client. Contributors are expected to adhere
147
+ to the [code of conduct](https://github.com/[USERNAME]/purple-client/blob/main/CODE_OF_CONDUCT.md).
36
148
 
37
149
  ## License
38
150
 
39
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
40
-
41
- ## Code of Conduct
151
+ The gem is available as open source under the terms of the
152
+ [MIT License](https://opensource.org/licenses/MIT).
42
153
 
43
- Everyone interacting in the Purple::Client project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/purple-client/blob/main/CODE_OF_CONDUCT.md).
data/lib/purple/client.rb CHANGED
@@ -43,8 +43,8 @@ module Purple
43
43
  end
44
44
  end
45
45
 
46
- def path(name, method: :get)
47
- path = Path.new(name:, parent: @parent_path, method:, client: self)
46
+ def path(name, method: :get, is_param: false)
47
+ path = Path.new(name:, parent: @parent_path, method:, client: self, is_param:)
48
48
 
49
49
  @paths ||= []
50
50
  @paths << path
@@ -62,10 +62,20 @@ module Purple
62
62
  def root_method(method_name)
63
63
  current_path = @parent_path
64
64
 
65
- define_singleton_method method_name do |**args, &block|
66
- params = current_path.request.params.call(**args) if current_path.request.params.is_a?(Proc)
65
+ define_singleton_method method_name do |*call_args, **kw_args, &block|
66
+ if current_path.is_param
67
+ value = kw_args[current_path.name] || call_args.first
68
+ current_path.with(value)
69
+ kw_args[current_path.name] = value
70
+ end
67
71
 
68
- current_path.execute(params, args, &block)
72
+ callback_arguments = additional_callback_arguments.map do |arg|
73
+ kw_args.delete(arg)
74
+ end
75
+
76
+ params = current_path.request.params.call(**kw_args) if current_path.request.params.is_a?(Proc)
77
+
78
+ current_path.execute(params, kw_args, *callback_arguments, &block)
69
79
  end
70
80
  end
71
81
 
@@ -106,6 +116,14 @@ module Purple
106
116
  super
107
117
  end
108
118
  end
119
+
120
+ def additional_callback_arguments(*array)
121
+ if array.empty?
122
+ @additional_callback_arguments || []
123
+ else
124
+ @additional_callback_arguments = array
125
+ end
126
+ end
109
127
  end
110
128
  end
111
129
  end
data/lib/purple/path.rb CHANGED
@@ -9,24 +9,44 @@ module Purple
9
9
  option :client
10
10
  option :name
11
11
  option :parent
12
+ option :is_param, default: -> { false }
12
13
  option :children, default: -> { [] }
13
14
  option :method, optional: true
14
15
  option :request, optional: true, default: -> { Purple::Request.new }
15
16
  option :responses, optional: true, default: -> { [] }
16
17
 
17
18
  def full_path
18
- parent.nil? ? name : "#{parent.full_path}/#{name}"
19
+ current_path = is_param ? @param_value : name
20
+ parent.nil? ? current_path : "#{parent.full_path}/#{current_path}"
21
+ end
22
+
23
+ def with(*args)
24
+ @param_value = args.first
19
25
  end
20
26
 
21
27
  def method_missing(method_name, *args, &)
22
28
  if children.any? { |child| child.name == method_name }
23
- children.find { |child| child.name == method_name }
29
+ child = children.find { |child| child.name == method_name }
30
+
31
+ if child.is_param
32
+ child.with(*args)
33
+ end
34
+
35
+ if child.children.any?
36
+ child
37
+ else
38
+ callback_arguments = additional_callback_arguments.map do |arg|
39
+ kw_args.delete(arg)
40
+ end
41
+
42
+ child.execute(*args, *callback_arguments)
43
+ end
24
44
  else
25
45
  super
26
46
  end
27
47
  end
28
48
 
29
- def execute(params = {}, args = {})
49
+ def execute(params = {}, args = {}, *callback_arguments)
30
50
  headers = {
31
51
  'Accept' => 'application/json',
32
52
  'Content-Type' => 'application/json'
@@ -43,7 +63,12 @@ module Purple
43
63
  conn.headers = headers
44
64
  end
45
65
 
66
+ unless client.domain.start_with?('http')
67
+ raise ArgumentError, "Invalid URL: #{client.domain}. Ensure you have set protocol (http/https) in the client domain."
68
+ end
69
+
46
70
  url = "#{client.domain}/#{full_path}"
71
+
47
72
  response = case method
48
73
  when :get
49
74
  connection.get(url, params)
@@ -64,7 +89,7 @@ module Purple
64
89
  {}
65
90
  end
66
91
 
67
- client.callback&.call(url, params, headers, JSON.parse(response.body))
92
+ client.callback&.call(url, params, headers, JSON.parse(response.body), *callback_arguments)
68
93
 
69
94
  if block_given?
70
95
  yield(resp_structure.status, object)
@@ -16,7 +16,8 @@ class Purple::Response
16
16
  forbidden: 403,
17
17
  not_found: 404,
18
18
  too_many_requests: 429,
19
- internal_server_error: 500
19
+ internal_server_error: 500,
20
+ gateway_timeout: 504,
20
21
  }.freeze
21
22
 
22
23
  def status_code
@@ -82,7 +82,12 @@ class Purple::Responses::Body
82
82
  check_structure!(item, value[0])
83
83
  end
84
84
  else
85
- check_type!(object, key, value)
85
+ if object.nil?
86
+ raise BodyStructureMismatchError.new(key, value, nil, object),
87
+ "Expected a non-nil value for '#{key}' in response body. Expected response structure: #{substructure}"
88
+ else
89
+ check_type!(object, key, value)
90
+ end
86
91
  end
87
92
  end
88
93
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Purple
4
- VERSION = "0.1.2"
4
+ VERSION = "0.1.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.2
4
+ version: 0.1.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-01-18 00:00:00.000000000 Z
11
+ date: 2025-06-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-initializer