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 +4 -4
- data/README.md +127 -17
- data/lib/purple/client.rb +23 -5
- data/lib/purple/path.rb +29 -4
- data/lib/purple/response.rb +2 -1
- data/lib/purple/responses/body.rb +6 -1
- data/lib/purple/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 426284067d37937cf20cc78132fc5286f4d691ae66cb25177f01c7219e844667
|
4
|
+
data.tar.gz: e08e9819a221659f94b9bbb54b41aa59a23ee1bc7fb2b8ca829e5a8039173325
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0eb4508617a8198b6dd0cddfef087aabe63b65c4a8825bfe759caf379aa2a3adbb0b7705181363a937d2fa818b7d77458da4dcd244ba0da03ce2a0184d6df2cd
|
7
|
+
data.tar.gz: 22df3ed5def051f3a9fb372143a19e830276cd70a18756940ce1ec807b732273353788128c818fe5d3c303081b09d0904d9afd8bac281151e620d1a1e1ef4154
|
data/README.md
CHANGED
@@ -1,43 +1,153 @@
|
|
1
1
|
# Purple::Client
|
2
2
|
|
3
|
-
|
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
|
-
|
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
|
10
|
+
bundle add purple-client
|
15
11
|
```
|
16
12
|
|
17
|
-
|
13
|
+
Or install it manually with:
|
18
14
|
|
19
15
|
```bash
|
20
|
-
gem install
|
16
|
+
gem install purple-client
|
21
17
|
```
|
22
18
|
|
23
19
|
## Usage
|
24
20
|
|
25
|
-
|
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
|
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`.
|
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
|
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
|
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
|
66
|
-
|
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
|
-
|
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
|
-
|
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)
|
data/lib/purple/response.rb
CHANGED
@@ -82,7 +82,12 @@ class Purple::Responses::Body
|
|
82
82
|
check_structure!(item, value[0])
|
83
83
|
end
|
84
84
|
else
|
85
|
-
|
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
|
data/lib/purple/version.rb
CHANGED
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.
|
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-
|
11
|
+
date: 2025-06-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dry-initializer
|