active_call 0.2.0 → 0.3.0

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: e129c0113e02eef8e739a830213f343eddb585c6798f4237f127256b1a5ac419
4
- data.tar.gz: 550cdfc7e5285f9dc353003eb570fc89ae1d36d54025af035ab2a4751b61658a
3
+ metadata.gz: 31e782b9a67b967ad37abe80818b43aa445517aa99a4830e2ec158c67404922e
4
+ data.tar.gz: 587d98f44af3c0a36198ffb76367bf30f0b8eb22b342d0d3d361c452452faf7a
5
5
  SHA512:
6
- metadata.gz: 91d837ea7bead55372e4eaf777f84f35ac4ad3a9183633e9bc9042d14cdf26ae02c425a30391ff4dc85116d66d38edb62202fa114c5657c9c5bee60fa02bde83
7
- data.tar.gz: 4025432ebc6ef1dbf2cbe5129b4e235808ab91ded26d192c04b7253bca008423e6d054808e3f774421e86bc42465e005ee0e4bfd26241ab19e685fc6ffc219d2
6
+ metadata.gz: 4354d1137d8dfc1dae8570ccc6fb1324f2999d9b099c3f2a0e1da1fe4b93aa99b39692029d6169263736772e8eadce59376f0cd25d9812da38080f7309d06502
7
+ data.tar.gz: a2875e4fd4b26027584f4c830befb94ea9dc799fbc70aacbe791d44a39fa679ff56817a22c25fa5816bb715a1d6555f9717332026410a6e1af3c66f7ae1282da
data/.rubocop.yml CHANGED
@@ -18,10 +18,17 @@ AllCops:
18
18
  Layout/LineEndStringConcatenationIndentation:
19
19
  EnforcedStyle: indented
20
20
 
21
+ Layout/LineLength:
22
+ AllowedPatterns:
23
+ - '#'
24
+
21
25
  Lint/MissingSuper:
22
26
  AllowedParentClasses:
23
27
  - ActiveCall::Base
24
28
 
29
+ Metrics/AbcSize:
30
+ Enabled: false
31
+
25
32
  Metrics/BlockLength:
26
33
  Exclude:
27
34
  - spec/*/**.rb
data/CHANGELOG.md CHANGED
@@ -1,8 +1,14 @@
1
- ## [Unreleased]
1
+ ## [0.3.0] - 2025-03-31
2
+
3
+ - Added `validate on: :request` which runs after `before_call` and before invoking `call`'.
4
+
5
+ ## [0.2.1] - 2025-03-25
6
+
7
+ - Gemspec `changelog_uri` fixed.
2
8
 
3
9
  ## [0.2.0] - 2025-03-20
4
10
 
5
- - Added method `.call!` with a bang, which will raise an `ActiveCall::ValidationError` exception when validation fails and an `ActiveCall::RequestError` exception when errors were added to the service object in the `after_call` callback.
11
+ - Added method `.call!` with a bang, which will raise an `ActiveCall::ValidationError` exception when validation fails and an `ActiveCall::RequestError` exception when errors were added to the service object in the `validate on: :response` block.
6
12
  - Use new method `success?` instead of `valid?`.
7
13
  - Method `valid?` will return `true` if the service object passed validation and was able to make the `call` method.
8
14
  - Use `validate, on: :response` to validate the response object.
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Active Call
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/active_call.svg?icon=si%3Arubygems)](https://badge.fury.io/rb/active_call)
4
+
3
5
  Active Call provides a standardized way to create service objects.
4
6
 
5
7
  ## Installation
@@ -28,17 +30,19 @@ Each service object must define only one public method named `call`.
28
30
 
29
31
  1. **Before** invoking `call`.
30
32
 
31
- - Validate the request with `validates`.
33
+ - Validate the service with `validates`.
32
34
 
33
35
  - Use the `before_call` hook to set up anything **after validation** passes.
34
36
 
37
+ - Validate the request with `validate on: :request`.
38
+
35
39
  2. **During** `call` invocation.
36
40
 
37
41
  - A `response` attribute gets set with the result of the `call` method.
38
42
 
39
43
  3. **After** invoking `call`.
40
44
 
41
- - Validate the response with `validate, on: :response`.
45
+ - Validate the response with `validate on: :response`.
42
46
 
43
47
  - Use the `after_call` hook to set up anything **after response validation** passes.
44
48
 
@@ -49,16 +53,20 @@ Define a service object with optional validations and callbacks.
49
53
  ```ruby
50
54
  require 'active_call'
51
55
 
52
- class YourGemName::SomeResource::CreateService < ActiveCall::Base
53
- attr_reader :message
56
+ class YourGem::SomeResource::CreateService < ActiveCall::Base
57
+ attr_reader :message, :another_service
54
58
 
55
59
  validates :message, presence: true
56
60
 
61
+ validate on: :request do
62
+ errors.merge!(another_service.errors) unless another_service.success?
63
+ end
64
+
57
65
  validate on: :response do
58
66
  errors.add(:message, :invalid, message: 'cannot be baz') if response[:foo] == 'baz'
59
67
  end
60
68
 
61
- before_call :strip_message
69
+ before_call :call_another_service, :strip_message
62
70
 
63
71
  after_call :log_response
64
72
 
@@ -72,6 +80,10 @@ class YourGemName::SomeResource::CreateService < ActiveCall::Base
72
80
 
73
81
  private
74
82
 
83
+ def call_another_service
84
+ @another_service = YourGem::SomeResource::GetService.call(id: '1')
85
+ end
86
+
75
87
  def strip_message
76
88
  @message.strip!
77
89
  end
@@ -82,12 +94,12 @@ class YourGemName::SomeResource::CreateService < ActiveCall::Base
82
94
  end
83
95
  ```
84
96
 
85
- ### Using `.call`
97
+ ### Using `call`
86
98
 
87
99
  You will get an **errors** object when validation fails.
88
100
 
89
101
  ```ruby
90
- service = YourGemName::SomeResource::CreateService.call(message: '')
102
+ service = YourGem::SomeResource::CreateService.call(message: '')
91
103
  service.success? # => false
92
104
  service.errors # => #<ActiveModel::Errors [#<ActiveModel::Error attribute=message, type=blank, options={}>]>
93
105
  service.errors.full_messages # => ["Message can't be blank"]
@@ -97,7 +109,7 @@ service.response # => nil
97
109
  A **response** object on a successful `call` invocation.
98
110
 
99
111
  ```ruby
100
- service = YourGemName::SomeResource::CreateService.call(message: ' bar ')
112
+ service = YourGem::SomeResource::CreateService.call(message: ' bar ')
101
113
  service.success? # => true
102
114
  service.response # => {:foo=>"bar"}
103
115
  ```
@@ -105,20 +117,20 @@ service.response # => {:foo=>"bar"}
105
117
  And an **errors** object if you added errors during the `validate, on: :response` validation.
106
118
 
107
119
  ```ruby
108
- service = YourGemName::SomeResource::CreateService.call(message: 'baz')
120
+ service = YourGem::SomeResource::CreateService.call(message: 'baz')
109
121
  service.success? # => false
110
122
  service.errors # => #<ActiveModel::Errors [#<ActiveModel::Error attribute=message, type=invalid, options={:message=>"cannot be baz"}>]>
111
123
  service.errors.full_messages # => ["Message cannot be baz"]
112
124
  service.response # => {:foo=>"baz"}
113
125
  ```
114
126
 
115
- ### Using `.call!`
127
+ ### Using `call!`
116
128
 
117
129
  An `ActiveCall::ValidationError` **exception** gets raised when validation fails.
118
130
 
119
131
  ```ruby
120
132
  begin
121
- service = YourGemName::SomeResource::CreateService.call!(message: '')
133
+ service = YourGem::SomeResource::CreateService.call!(message: '')
122
134
  rescue ActiveCall::ValidationError => exception
123
135
  exception.errors # => #<ActiveModel::Errors [#<ActiveModel::Error attribute=message, type=blank, options={}>]>
124
136
  exception.errors.full_messages # => ["Message can't be blank"]
@@ -128,7 +140,7 @@ end
128
140
  A **response** object on a successful `call` invocation.
129
141
 
130
142
  ```ruby
131
- service = YourGemName::SomeResource::CreateService.call!(message: ' bar ')
143
+ service = YourGem::SomeResource::CreateService.call!(message: ' bar ')
132
144
  service.success? # => true
133
145
  service.response # => {:foo=>"bar"}
134
146
  ```
@@ -137,7 +149,7 @@ And an `ActiveCall::RequestError` **exception** gets raised if you added errors
137
149
 
138
150
  ```ruby
139
151
  begin
140
- service = YourGemName::SomeResource::CreateService.call!(message: 'baz')
152
+ service = YourGem::SomeResource::CreateService.call!(message: 'baz')
141
153
  rescue ActiveCall::RequestError => exception
142
154
  exception.errors # => #<ActiveModel::Errors [#<ActiveModel::Error attribute=message, type=invalid, options={:message=>"cannot be baz"}>]>
143
155
  exception.errors.full_messages # => ["Message cannot be baz"]
@@ -150,17 +162,17 @@ end
150
162
  If you have secrets, use a **configuration** block.
151
163
 
152
164
  ```ruby
153
- class YourGemName::BaseService < ActiveCall::Base
165
+ class YourGem::BaseService < ActiveCall::Base
154
166
  self.abstract_class = true
155
167
 
156
168
  config_accessor :api_key, default: ENV['API_KEY'], instance_writer: false
157
169
  end
158
170
  ```
159
171
 
160
- Then in your application code you can overwite the configuration defaults.
172
+ Then in your application code you can override the configuration defaults.
161
173
 
162
174
  ```ruby
163
- YourGemName::BaseService.configure do |config|
175
+ YourGem::BaseService.configure do |config|
164
176
  config.api_key = Rails.application.credentials.api_key || ENV['API_KEY']
165
177
  end
166
178
  ```
@@ -170,7 +182,7 @@ And implement a service object like so.
170
182
  ```ruby
171
183
  require 'net/http'
172
184
 
173
- class YourGemName::SomeResource::CreateService < YourGemName::BaseService
185
+ class YourGem::SomeResource::CreateService < YourGem::BaseService
174
186
  def call
175
187
  Net::HTTP.get_response(URI("http://example.com/api?#{URI.encode_www_form(api_key: api_key)}"))
176
188
  end
@@ -199,9 +211,14 @@ spec.add_dependency 'active_call'
199
211
 
200
212
  Now start adding your service objects in the `lib` directory and make sure they inherit from `ActiveCall::Base`.
201
213
 
214
+ ## Active Call Extensions
215
+
216
+ - [Active Call - API](https://rubygems.org/gems/active_call-api)
217
+
202
218
  ## Gems Using Active Call
203
219
 
204
- - [Zoho Sign](https://github.com/kobusjoubert/zoho_sign)
220
+ - [Active Call - nCino KYC DocFox](https://rubygems.org/gems/active_call-doc_fox)
221
+ - [Active Call - Zoho Sign](https://rubygems.org/gems/active_call-zoho_sign)
205
222
 
206
223
  ## Development
207
224
 
@@ -14,13 +14,13 @@ class ActiveCall::Base
14
14
  # Abstract classes are not meant to be instantiated directly, but rather inherited from.
15
15
  # The `call` method doesn't need to be implemented in abstract classes.
16
16
  #
17
- # Example:
17
+ # ==== Examples
18
18
  #
19
- # class YourGemName::BaseService < ActiveCall::Base
19
+ # class YourGem::BaseService < ActiveCall::Base
20
20
  # self.abstract_class = true
21
21
  # end
22
22
  #
23
- # class YourGemName::SomeResource::CreateService < YourGemName::BaseService
23
+ # class YourGem::SomeResource::CreateService < YourGem::BaseService
24
24
  # def call
25
25
  # # Implementation specific to this service.
26
26
  # end
@@ -32,32 +32,97 @@ class ActiveCall::Base
32
32
  @abstract_class == true
33
33
  end
34
34
 
35
- # TODO: Refactor `call` and `call!`. The only differences are the two lines raising exceptions.
35
+ # TODO: Refactor `call` and `call!`. The only differences are the lines raising exceptions.
36
+
37
+ # Using `call`
38
+ #
39
+ # ==== Examples
40
+ #
41
+ # You will get an `errors` object when validation fails.
42
+ #
43
+ # service = YourGem::SomeResource::CreateService.call(message: '')
44
+ # service.success? # => false
45
+ # service.errors # => #<ActiveModel::Errors [#<ActiveModel::Error attribute=message, type=blank, options={}>]>
46
+ # service.errors.full_messages # => ["Message can't be blank"]
47
+ # service.response # => nil
48
+ #
49
+ # A `response` object on a successful `call` invocation.
50
+ #
51
+ # service = YourGem::SomeResource::CreateService.call(message: ' bar ')
52
+ # service.success? # => true
53
+ # service.response # => {:foo=>"bar"}
54
+ #
55
+ # And an `errors` object if you added errors during the `validate, on: :response` validation.
56
+ #
57
+ # service = YourGem::SomeResource::CreateService.call(message: 'baz')
58
+ # service.success? # => false
59
+ # service.errors # => #<ActiveModel::Errors [#<ActiveModel::Error attribute=message, type=invalid, options={:message=>"cannot be baz"}>]>
60
+ # service.errors.full_messages # => ["Message cannot be baz"]
61
+ # service.response # => {:foo=>"baz"}
62
+ #
36
63
  def call(...)
37
64
  service_object = new(...)
38
65
  service_object.instance_variable_set(:@bang, false)
39
- return service_object if service_object.invalid?(except_on: :response)
66
+ return service_object if service_object.invalid?(except_on: [:request, :response])
40
67
 
41
68
  service_object.run_callbacks(:call) do
42
69
  next if service_object.is_a?(Enumerable)
43
70
 
71
+ service_object.validate(:request)
72
+ return service_object unless service_object.success?
73
+
44
74
  service_object.instance_variable_set(:@response, service_object.call)
45
75
  service_object.validate(:response)
46
-
47
76
  return service_object unless service_object.success?
48
77
  end
49
78
 
50
79
  service_object
51
80
  end
52
81
 
82
+ # Using `call!`
83
+ #
84
+ # ==== Examples
85
+ #
86
+ # An `ActiveCall::ValidationError` exception gets raised when validation fails.
87
+ #
88
+ # begin
89
+ # service = YourGem::SomeResource::CreateService.call!(message: '')
90
+ # rescue ActiveCall::ValidationError => exception
91
+ # exception.errors # => #<ActiveModel::Errors [#<ActiveModel::Error attribute=message, type=blank, options={}>]>
92
+ # exception.errors.full_messages # => ["Message can't be blank"]
93
+ # end
94
+ #
95
+ # A `response` object on a successful `call` invocation.
96
+ #
97
+ # service = YourGem::SomeResource::CreateService.call!(message: ' bar ')
98
+ # service.success? # => true
99
+ # service.response # => {:foo=>"bar"}
100
+ #
101
+ # And an `ActiveCall::RequestError` exception gets raised if you added errors during the `validate, on: :response`
102
+ # validation.
103
+ #
104
+ # begin
105
+ # service = YourGem::SomeResource::CreateService.call!(message: 'baz')
106
+ # rescue ActiveCall::RequestError => exception
107
+ # exception.errors # => #<ActiveModel::Errors [#<ActiveModel::Error attribute=message, type=invalid, options={:message=>"cannot be baz"}>]>
108
+ # exception.errors.full_messages # => ["Message cannot be baz"]
109
+ # exception.response # => {:foo=>"baz"}
110
+ # end
111
+ #
53
112
  def call!(...)
54
113
  service_object = new(...)
55
114
  service_object.instance_variable_set(:@bang, true)
56
- raise ActiveCall::ValidationError, service_object.errors if service_object.invalid?(except_on: :response)
115
+
116
+ if service_object.invalid?(except_on: [:request, :response])
117
+ raise ActiveCall::ValidationError, service_object.errors
118
+ end
57
119
 
58
120
  service_object.run_callbacks(:call) do
59
121
  next if service_object.is_a?(Enumerable)
60
122
 
123
+ service_object.validate(:request)
124
+ raise ActiveCall::RequestError.new(nil, service_object.errors) unless service_object.success?
125
+
61
126
  service_object.instance_variable_set(:@response, service_object.call)
62
127
  service_object.validate(:response)
63
128
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveCall
4
- VERSION = '0.2.0'
4
+ VERSION = '0.3.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_call
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kobus Joubert
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-03-20 00:00:00.000000000 Z
11
+ date: 2025-03-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -66,7 +66,7 @@ metadata:
66
66
  rubygems_mfa_required: 'true'
67
67
  homepage_uri: https://github.com/kobusjoubert/active_call
68
68
  source_code_uri: https://github.com/kobusjoubert/active_call
69
- changelog_uri: https://github.com/kobusjoubert/active_call/CHANGELOG.md
69
+ changelog_uri: https://github.com/kobusjoubert/active_call/blob/main/CHANGELOG.md
70
70
  post_install_message:
71
71
  rdoc_options: []
72
72
  require_paths: