service_core 0.1.3 → 1.0.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: 01d926ff29ac3ea5c0ecb4f7a754ea6531732cfe8bfe33591f17a0827132fd21
4
- data.tar.gz: b79448edd6a9a4900430f246b0410362782d197a0b9318add7e6f7d63d71df1b
3
+ metadata.gz: d9e82b7404a555e1d2b60d559e82b3f37a2cfe829ad947687f5e290252b87712
4
+ data.tar.gz: a170ee5ab04fe57be88786b027b07ca1db31732fa316eafb0a36b10c827c078c
5
5
  SHA512:
6
- metadata.gz: b192d8ca5f4439ca1d178697c70ed5ed1e288108c23c72b1aafa8ac1e7a17124f97c6b8f95978615f62e0f58e536c1a1f48cddc6b3e6ab39e5402f314ad3fa36
7
- data.tar.gz: b855792ae11ac1a308004437e01374daac234ae4588416dc936eb2d5ac9cf973b03c2435a48d515e3a577cc29016dcbcdd88939d4c4b43d6d98b7b2f0cdd1109
6
+ metadata.gz: 2a109447da13844396e83db86186849598c0ad4b55b845d07fb6c8c1592aa7a10dc69b135cd84785b407588580871470bf94132158e60c2b237b7a05fdd099ff
7
+ data.tar.gz: b2a495f969e193f7f6a9403c32fd88cf7586f49150268d61bbb600ea565ed039107aa1edc119cf8c5ebc37614f303a10471230292294c420930d3f19241c6539
data/.rubocop.yml CHANGED
@@ -1,10 +1,25 @@
1
+ plugins:
2
+ - rubocop-rake
3
+
4
+ require:
5
+ - rubocop-rspec
6
+
1
7
  AllCops:
2
- TargetRubyVersion: 2.6
8
+ TargetRubyVersion: 3.1
9
+ NewCops: enable
10
+ SuggestExtensions: false
11
+ Exclude:
12
+ - "gemfiles/**/*"
13
+ - "vendor/**/*"
3
14
 
4
15
  Style/StringLiterals:
5
16
  Enabled: true
6
17
  EnforcedStyle: double_quotes
7
18
 
19
+ # Per project style, frozen_string_literal magic comments are not used.
20
+ Style/FrozenStringLiteralComment:
21
+ Enabled: false
22
+
8
23
  Style/StringLiteralsInInterpolation:
9
24
  Enabled: true
10
25
  EnforcedStyle: double_quotes
@@ -16,4 +31,22 @@ Style/Documentation:
16
31
  Enabled: false
17
32
 
18
33
  Metrics/BlockLength:
19
- Enabled: false
34
+ Enabled: false
35
+
36
+ RSpec/MultipleExpectations:
37
+ Enabled: false
38
+
39
+ RSpec/ExampleLength:
40
+ Enabled: false
41
+
42
+ RSpec/MessageSpies:
43
+ Enabled: false
44
+
45
+ RSpec/VerifiedDoubleReference:
46
+ Enabled: false
47
+
48
+ # add_error_and_validate is part of the public API and intentionally returns
49
+ # the boolean result of valid?, but its name describes the action it performs.
50
+ Naming/PredicateMethod:
51
+ AllowedMethods:
52
+ - add_error_and_validate
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.4.9
data/Appraisals ADDED
@@ -0,0 +1,19 @@
1
+ # Each appraisal pins ActiveModel/ActiveSupport to a maintained Rails release.
2
+ # Older Rails versions (6.1, 7.0, 7.1) are still permitted by the gemspec
3
+ # floor but are not exercised here because their upstream support window
4
+ # has ended.
5
+
6
+ appraise "rails-7.2" do
7
+ gem("activemodel", "~> 7.2.0")
8
+ gem("activesupport", "~> 7.2.0")
9
+ end
10
+
11
+ appraise "rails-8.0" do
12
+ gem("activemodel", "~> 8.0.0")
13
+ gem("activesupport", "~> 8.0.0")
14
+ end
15
+
16
+ appraise "rails-8.1" do
17
+ gem("activemodel", "~> 8.1.0")
18
+ gem("activesupport", "~> 8.1.0")
19
+ end
data/CHANGELOG.md CHANGED
@@ -1,5 +1,83 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
1
8
  ## [Unreleased]
2
9
 
10
+ ## [1.0.0] - 2026-05-14
11
+
12
+ ### Added
13
+
14
+ - `ServiceCore::Response`: a Hash-compatible value object backing
15
+ `service.response` / `service.output` and the return of `#call`. Adds
16
+ named accessors (`response.status`, `response.data`, `response.message`,
17
+ `response.errors`), Hash-style `[]` / `[]=` / `==`, `dig`, `fetch`,
18
+ `each_pair`, and `as_json` / `to_json`. Callers that previously
19
+ reached for `service.output[:key]` on the 0.1.0 Hash output continue
20
+ to work unchanged.
21
+ - `service.response` as an alias for `service.output`.
22
+ - `ServiceCore::FieldSet`: an immutable snapshot for `service.fields`.
23
+ Each declared symbol field is exposed as a real method (e.g.
24
+ `service.fields.first_name`); call `to_h` for a plain Hash.
25
+ - `field` raises `ServiceCore::ReservedFieldName` when the declared name
26
+ would shadow a ServiceCore method. The reserved names are `:call`,
27
+ `:errors`, `:fields`, `:output`, `:perform` and `:response`. Previously
28
+ these names silently overrode gem internals (most dangerously
29
+ `:errors`, which broke `ActiveModel::Validations`).
30
+ - `ServiceCore::Error` hierarchy with concrete subclasses
31
+ `ServiceCore::InvalidKey` (raised by `Response#[]` / `[]=` on a
32
+ non-allowed key) and `ServiceCore::ReservedFieldName`. `Error` itself
33
+ existed since 0.1.0 but was never raised; `rescue ServiceCore::Error`
34
+ now actually catches things.
35
+ - Cross-Rails test matrix via `appraisal`: Rails 7.2, 8.0 and 8.1.
36
+ - CI matrix across Ruby 3.3, 3.4 and 4.0 (excluding Ruby 4.0 + Rails 7.2).
37
+
38
+ ### Changed
39
+
40
+ - The mixin that provides `success_response`, `error_response` and
41
+ `formatted_response` is renamed from `ServiceCore::Response` to
42
+ `ServiceCore::Responder`. The `Response` name now refers to the value
43
+ object; the `Responder` mixin is the builder. Both modules are internal
44
+ implementation details consumed via `include ServiceCore`, so users
45
+ who only ever `include ServiceCore` are unaffected. Anyone including
46
+ `ServiceCore::Response` directly must switch to
47
+ `include ServiceCore::Responder`.
48
+ - Gemspec: `required_ruby_version` raised to `>= 3.1.0` (Ruby 2.7 and 3.0 are EOL).
49
+ - Gemspec: ActiveModel / ActiveSupport range widened to `>= 6.1, < 9.0`.
50
+ - RuboCop bumped to `~> 1.86` with `rubocop-rake` added as a plugin.
51
+ - Replaced `byebug` (unmaintained on Ruby 3+) with the stdlib `debug` gem.
52
+ - Removed `# frozen_string_literal: true` magic comments project-wide in
53
+ favour of the project style; `VERSION` is now explicitly `.freeze`d.
54
+ - `add_error_and_validate` now forwards its `options` argument to
55
+ `add_error` (and through to `ActiveModel::Errors#add`), matching its
56
+ declared signature.
57
+ - Internal cleanups: `Output#initialize` now calls `super()`, the
58
+ `StepValidation` validator helpers are private, and `auto_assign_status`
59
+ no longer has the redundant elsif clauses.
60
+ - The class-level `fields_defined` registry is renamed to `field_names`
61
+ and is now a `Set` of declared names rather than a Hash mapping names
62
+ to defaults. Typed defaults are still applied through
63
+ `ActiveModel::Attributes`; the old Hash was a Set with a confusing
64
+ name and shape.
65
+ - `service.fields` no longer behaves like a Hash. The new `FieldSet`
66
+ exposes named accessors plus `to_h`. Callers that did
67
+ `service.fields[:name]` should use `service.fields.name`; iteration
68
+ goes through `service.fields.to_h.each`.
69
+
70
+ ### Fixed
71
+
72
+ - `Output#set_output` no longer drops legitimate falsy values. Previously
73
+ `false`, `0`, and `""` were silently discarded; only `nil` is now skipped.
74
+ - `Responder#formatted_response` no longer gates its writes on
75
+ ActiveSupport `present?`. `success_response(data: false)` now records
76
+ `data: false`.
77
+ - `Base.field` no longer swallows positional defaults of `false` or `nil`.
78
+ The arity of the positional arguments is used instead of `||`, so
79
+ `field :enabled, :boolean, false` actually defaults to `false`.
80
+
3
81
  ## [0.1.0] - 2024-07-17
4
82
 
5
- - Initial release
83
+ - Initial release.
data/README.md CHANGED
@@ -1,27 +1,50 @@
1
1
  # ServiceCore
2
2
 
3
- ServiceCore provides a standardized way to define and use service objects in your Ruby and Rails applications. It includes support for defining fields, validations, responses, and logging.
3
+ ServiceCore is a small Ruby gem that gives service objects a shared shape. Every service exposes a single `call` method and returns the same four-key response, regardless of who wrote it. The idea behind the shape is unpacked in [The Shape of a Service Response](https://agnosticlogic.substack.com/p/the-shape-of-a-service-response).
4
+
5
+ - Four-key response contract: **status**, **data**, **message**, **errors**.
6
+ - Field declarations with types, defaults, and ActiveModel validations.
7
+ - Step-by-step validation that survives `valid?` calls.
8
+ - Hash-compatible value objects (`Response`, `FieldSet`) instead of raw hashes.
9
+ - Works on Ruby >= 3.1 and Rails (ActiveModel/ActiveSupport) 6.1 through 8.x.
4
10
 
5
11
  ## Installation
6
- Install the gem and add to the application's Gemfile by executing:
7
12
 
8
13
  ```sh
9
14
  bundle add service_core
10
15
  ```
11
- If bundler is not being used to manage dependencies, install the gem by executing:
16
+
17
+ Or add it to your Gemfile:
18
+
19
+ ```ruby
20
+ gem "service_core"
21
+ ```
22
+
23
+ If you are not using Bundler:
12
24
 
13
25
  ```sh
14
26
  gem install service_core
15
27
  ```
16
- ## Usage
17
28
 
18
- ### Defining a Service
29
+ ## The four-key response
19
30
 
20
- To define a new service, include the `ServiceCore` module in your service class and define your fields and the perform method.
21
- ```ruby
22
- # app/services/my_service.rb
31
+ Every service responds with at most four keys:
23
32
 
24
- class MyService
33
+ | Key | Purpose |
34
+ | --------- | ---------------------------------------------------------------------- |
35
+ | `status` | Machine-readable signal (`"success"`, `"error"`, or any custom state). |
36
+ | `data` | The payload the caller asked for. |
37
+ | `message` | High-level human context, distinct from per-field error detail. |
38
+ | `errors` | Structured error detail (Hash, Array, ActiveModel::Errors, ...). |
39
+
40
+ The shape is enforced; the value types are not. Writing a key other than these four raises `ServiceCore::InvalidKey`.
41
+
42
+ ## Defining a service
43
+
44
+ Include `ServiceCore` in your class and implement `perform`:
45
+
46
+ ```ruby
47
+ class GreetService
25
48
  include ServiceCore
26
49
 
27
50
  field :first_name, :string
@@ -29,39 +52,137 @@ class MyService
29
52
  field :active, :boolean, default: true
30
53
 
31
54
  def perform
32
- success_response(message: "Hello, World", data: name)
55
+ success_response(message: "Hello, World", data: full_name)
33
56
  end
34
57
 
35
- def name
58
+ private
59
+
60
+ def full_name
36
61
  "#{first_name} #{last_name}"
37
62
  end
38
63
  end
39
64
  ```
40
65
 
41
- ### Using a Service
42
- Instantiate and call the service to execute it. The call method will validate the input, perform the operation, and return the output.
66
+ ## Calling a service
67
+
68
+ You can call a service either via `new(...).call` or via the `.call` shortcut on the class:
69
+
70
+ ```ruby
71
+ response = GreetService.new(first_name: "John", last_name: "Doe").call
72
+ puts response
73
+ # => {status: "success", message: "Hello, World", data: "John Doe"}
74
+
75
+ service = GreetService.call(first_name: "John", last_name: "Doe")
76
+ service.response
77
+ # => {status: "success", message: "Hello, World", data: "John Doe"}
78
+ ```
79
+
80
+ The instance method returns the response value object. The class-level `.call` returns the service instance, so you can also reach for `service.response` (or `service.output`, which is kept as an alias) after the fact.
81
+
82
+ ## `Response`: the value object
83
+
84
+ `service.response` (and the value returned from `#call`) is a `ServiceCore::Response`. It exposes both named accessors and Hash-style access, and serialises to JSON like the underlying hash:
85
+
86
+ ```ruby
87
+ response = GreetService.call(first_name: "John", last_name: "Doe").response
88
+
89
+ response.status # => "success"
90
+ response.data # => "John Doe"
91
+ response[:status] # => "success"
92
+ response == { status: "success", message: "Hello, World", data: "John Doe" } # => true
93
+ response.to_json # => '{"status":"success","message":"Hello, World","data":"John Doe"}'
94
+ ```
95
+
96
+ Writing or reading a key other than the four allowed raises `ServiceCore::InvalidKey`.
97
+
98
+ ## Declaring fields
99
+
100
+ `field` supports both typed and untyped declarations.
101
+
102
+ ```ruby
103
+ class MyService
104
+ include ServiceCore
105
+
106
+ field :first_name, :string # typed (ActiveModel::Attributes)
107
+ field :active, :boolean, default: true # typed with keyword default
108
+ field :enabled, :boolean, false # typed with positional default
109
+ field :payload # untyped, can be any object/hash/array
110
+ end
111
+ ```
112
+
113
+ Typed fields are backed by `ActiveModel::Attributes` and inherit its casting and default support.
114
+
115
+ The following names are reserved and cannot be used as field names because they would shadow methods the gem itself defines: `:call`, `:errors`, `:fields`, `:output`, `:perform`, `:response`. Declaring `field :errors` (for example) raises `ServiceCore::ReservedFieldName`.
116
+
117
+ ### Field snapshot via `FieldSet`
118
+
119
+ After construction, `service.fields` exposes an immutable snapshot of the declared fields and their values as a `ServiceCore::FieldSet`. Each declared symbol field is available as a real method; call `to_h` if you need a plain Hash.
120
+
121
+ ```ruby
122
+ service = GreetService.new(first_name: "John", last_name: "Doe")
123
+
124
+ service.fields.first_name # => "John"
125
+ service.fields.to_h # => { first_name: "John", last_name: "Doe", active: true }
126
+ ```
127
+
128
+ The snapshot is taken at `#initialize`, so it reflects the values at construction time. Live values are still available through each declared accessor (e.g. `service.first_name`).
129
+
130
+ ## Building responses
131
+
132
+ Three helpers cover almost every case.
133
+
134
+ ### `success_response`
135
+
136
+ ```ruby
137
+ def perform
138
+ success_response(message: "Hello, World", data: full_name)
139
+ end
140
+ # => {status: "success", message: "Hello, World", data: "John Doe"}
141
+ ```
142
+
143
+ Accepts `message` and `data`. Status is set to `"success"`.
144
+
145
+ ### `error_response`
146
+
147
+ ```ruby
148
+ def perform
149
+ error_response(message: "validation failure", errors: "last_name can't be blank")
150
+ end
151
+ # => {status: "error", message: "validation failure", errors: "last_name can't be blank"}
152
+ ```
153
+
154
+ Accepts `message` and `errors`. Status is set to `"error"`. `errors` can be a String, Hash, Array, or `ActiveModel::Errors` (which is normalised through `messages`).
155
+
156
+ ### `formatted_response`
157
+
158
+ For any status that isn't success or error.
159
+
160
+ ```ruby
161
+ def perform
162
+ formatted_response(status: "processed", message: "Already done", data: existing_record)
163
+ end
164
+ # => {status: "processed", message: "Already done", data: ...}
165
+ ```
166
+
167
+ Accepts `status`, `message`, `data`, and `errors`. Use this for `"pending"`, `"queued"`, `"processed"`, or any domain-specific status.
168
+
169
+ ### `set_output`
170
+
171
+ For finer-grained control, write a single key at a time:
172
+
43
173
  ```ruby
44
- service = MyService.new(first_name: "John", last_name: "Doe")
45
- result = service.call
46
- puts result
47
- # Output:
48
- # {
49
- # status: "success",
50
- # message: "Hello, World",
51
- #. data: "John Doe"
52
- # }
53
-
54
- puts service.output
55
- # Output:
56
- # {
57
- # status: "success",
58
- # message: "Hello, World",
59
- #. data: "John Doe"
60
- # }
61
- ```
62
-
63
- ### Validations
64
- You can define validation on the service and those will be invoked before service logic is invoked
174
+ def perform
175
+ set_output(:message, "Hello, World")
176
+ set_output(:data, full_name)
177
+ end
178
+ ```
179
+
180
+ If `status` is not set explicitly, it is auto-assigned to `"success"` when `errors` is blank, and `"error"` otherwise. `nil` is the only value treated as "not set"; `false`, `0`, and `""` are stored as-is.
181
+
182
+ ## Validations
183
+
184
+ Standard ActiveModel validations run before `perform`. If they fail, the response is filled in for you.
185
+
65
186
  ```ruby
66
187
  class MyService
67
188
  include ServiceCore
@@ -74,51 +195,43 @@ class MyService
74
195
  end
75
196
  end
76
197
 
77
- service = MyService.new(name: "")
78
- result = service.call
79
- puts result
80
- # Output:
81
- #{
82
- # status: "error",
83
- # message: "validation failure",
84
- # errors: { name: ["can't be blank"] }
85
- # }
198
+ MyService.new(name: "").call
199
+ # => {status: "error", message: "validation failure", errors: {name: ["can't be blank"]}}
86
200
  ```
87
201
 
88
- ### Step Validation
89
- You can perform validation at each step of service logic. This is helpful when result of previous step decides next logic.
202
+ ### Step validation
203
+
204
+ When the result of one step decides the next, `add_error_and_validate` lets you accumulate errors mid-`perform` without `valid?` wiping them.
205
+
90
206
  ```ruby
91
207
  class MyService
92
208
  include ServiceCore
93
-
209
+
94
210
  field :first_name, :string
95
211
  field :last_name, :string
96
- field :user
97
212
 
98
213
  validates :first_name, presence: true
99
- validates :user, presence: true
100
214
 
101
215
  def perform
102
216
  if last_name.blank?
103
217
  add_error_and_validate(:last_name, "can't be nil")
104
218
  return error_response(message: "validation failure", errors: errors)
105
219
  end
106
-
107
- success_response(data: { user: { id: 1 } })
220
+
221
+ success_response(data: { user: { id: 1 } })
108
222
  end
109
223
  end
110
224
 
111
- MyService.call(first_name: 'abc')
112
- # output:
113
- # {
114
- # status: "error",
115
- # message: "validation failure",
116
- # errors: { last_name: ["can't be nil"] }
117
- # }
225
+ MyService.call(first_name: "abc").response
226
+ # => {status: "error", message: "validation failure", errors: {last_name: ["can't be nil"]}}
118
227
  ```
119
228
 
120
- ### Logging Errors
121
- You can log errors using the `log_error` method.
229
+ `add_error_and_validate(attribute, message, options = {})` forwards `options` to `ActiveModel::Errors#add`, so options like `strict: true` are honoured.
230
+
231
+ ## Logging errors
232
+
233
+ `log_error(exception)` writes through the configured `ServiceCore.logger` and tags the message with the service class name.
234
+
122
235
  ```ruby
123
236
  class MyService
124
237
  include ServiceCore
@@ -126,80 +239,83 @@ class MyService
126
239
  field :name, :string
127
240
 
128
241
  def perform
129
- begin
130
- raise StandardError, "Something went wrong"
131
- rescue StandardError => e
132
- log_error(e)
133
- error_response(message: "Failed", errors: { base: [e.message] })
134
- end
242
+ raise StandardError, "Something went wrong"
243
+ rescue StandardError => e
244
+ log_error(e)
245
+ error_response(message: "Failed", errors: { base: [e.message] })
135
246
  end
136
247
  end
248
+ ```
249
+
250
+ ## Exceptions
137
251
 
138
- service = MyService.new(name: "World")
139
- result = service.call
140
- puts result
141
- # Output:
142
- # {
143
- # status: "error",
144
- # message: "Failed",
145
- # errors: { base: ["Something went wrong"] }
146
- # }
252
+ All gem-specific exceptions inherit from `ServiceCore::Error`, so a single rescue block can catch anything ServiceCore raises:
147
253
 
254
+ ```ruby
255
+ begin
256
+ MyService.call(...)
257
+ rescue ServiceCore::Error => e
258
+ # any gem-raised error
259
+ end
148
260
  ```
149
261
 
150
- ### Configuring the Logger
151
- You can configure the logger for the ServiceCore module.
262
+ The current concrete subclasses are:
263
+
264
+ - `ServiceCore::InvalidKey` — raised by `response[:not_allowed]` or `response[:not_allowed] = value` when the key is not one of the four allowed response keys.
265
+ - `ServiceCore::ReservedFieldName` — raised by `field :errors` (or any other reserved name) at class-definition time.
266
+
267
+ Two raises stay on stdlib classes: `Response#fetch` raises `KeyError` to match `Hash#fetch`, and the default `perform` raises `StandardError` until the service overrides it.
268
+
269
+ ## Configuration
270
+
152
271
  ```ruby
153
272
  ServiceCore.configure do |config|
154
- config.logger = Logger.new(STDOUT)
273
+ config.logger = Logger.new($stdout)
155
274
  end
156
275
  ```
157
276
 
158
- ### Custom Response
159
- use `formatted_response` method to return any other status other than `success` or `error`
160
- ```ruby
277
+ If you do not configure a logger, `ServiceCore.logger` defaults to `Rails.logger` when available, and otherwise to an `ActiveSupport::Logger` writing to `$stdout`.
161
278
 
162
- class MyService
163
- include ServiceCore
279
+ ## Stability
164
280
 
165
- field :first_name, :string
166
- field :last_name, :string
167
- field :active, :boolean, default: true
281
+ ServiceCore follows [Semantic Versioning](https://semver.org/). Starting with 1.0.0, the following are part of the public API and changes to them require a major version bump:
168
282
 
169
- def perform
170
- formatted_response(status: 'processed', message: "Hello, World", data: name)
171
- end
283
+ - The four-key response contract (`status`, `data`, `message`, `errors`).
284
+ - The Hash-compatible surface of `ServiceCore::Response` (`[]`, `[]=`, `==`, `to_h`, `to_s`, `inspect`, `keys`, `values`, `each`, `dig`, `fetch`, `key?` / `has_key?` / `include?`, `as_json`, `to_json`) and its named accessors (`status`, `data`, `message`, `errors`).
285
+ - The `ServiceCore::FieldSet` API (`to_h` and named accessors per declared symbol field).
286
+ - The service DSL: `include ServiceCore`, `field`, `validates`, `perform`, instance `#call` and class `.call`, `service.fields`, `service.response` / `service.output`.
287
+ - The response builders: `success_response`, `error_response`, `formatted_response`, `set_output`.
288
+ - The step-validation helpers: `add_error`, `add_error_and_validate`.
289
+ - The reserved field names: `:call`, `:errors`, `:fields`, `:output`, `:perform`, `:response`.
290
+ - The exception hierarchy under `ServiceCore::Error`.
291
+ - `ServiceCore.logger` and `ServiceCore.configure`.
172
292
 
173
- def name
174
- "#{first_name} #{last_name}"
175
- end
176
- end
293
+ The internals of `ServiceCore::Output`, the `Responder` mixin shape, and anything not listed above are implementation details and may change between any release.
294
+
295
+ ## Compatibility
296
+
297
+ - Ruby: 3.1 minimum; tested against 3.3 and 3.4 (and 4.0 against Rails 8.x).
298
+ - ActiveModel / ActiveSupport: `>= 6.1, < 9.0`; tested against Rails 7.2, 8.0, and 8.1 via [appraisal](https://github.com/thoughtbot/appraisal).
177
299
 
178
- service = MyService.new(first_name: "John", last_name: "Doe")
179
- result = service.call
180
- puts result
181
- # Output:
182
- # {
183
- # status: "processed",
184
- # message: "Hello, World",
185
- #. data: "John Doe"
186
- # }
300
+ ## Development
301
+
302
+ ```sh
303
+ bin/setup
304
+ bundle exec rspec
305
+ bundle exec rubocop
187
306
  ```
188
307
 
189
- `formatted_response` accepts following arguments:
190
- - status
191
- - message
192
- - data
193
- - errors
308
+ To run the spec suite against every supported Rails version:
309
+
310
+ ```sh
311
+ bundle exec appraisal install
312
+ bundle exec appraisal rspec
313
+ ```
194
314
 
195
315
  ## Contributing
196
316
 
197
- Bug reports and pull requests are welcome on GitHub at https://github.com/sehgalmayank001/service-core. 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/sehgalmayank001/service-core/blob/main/CODE_OF_CONDUCT.md).
317
+ Bug reports and pull requests are welcome on GitHub at [github.com/sehgalmayank001/service-core](https://github.com/sehgalmayank001/service-core). This project follows the [Contributor Covenant code of conduct](https://github.com/sehgalmayank001/service-core/blob/main/CODE_OF_CONDUCT.md).
198
318
 
199
319
  ## License
200
320
 
201
321
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
202
-
203
- ## Code of Conduct
204
-
205
- Everyone interacting in the ServiceCore project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/sehgalmayank001/service-core/blob/main/CODE_OF_CONDUCT.md).
data/Rakefile CHANGED
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  require "bundler/gem_tasks"
4
2
  require "rspec/core/rake_task"
5
3
 
@@ -16,6 +14,6 @@ task :release do
16
14
  version = `ruby -r ./lib/service_core/version -e "puts ServiceCore::VERSION"`.strip
17
15
  # sh "git add ."
18
16
  # sh "git commit -m 'Prepare for version #{version} release'"
19
- # sh "git tag v#{version}"
20
- # sh "git push origin main --tags"
21
- end
17
+ sh "git tag v#{version}"
18
+ sh "git push origin main --tags"
19
+ end
@@ -0,0 +1,18 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 13.0"
6
+ gem "activemodel", "~> 7.2.0"
7
+ gem "activesupport", "~> 7.2.0"
8
+
9
+ group :development, :test do
10
+ gem "appraisal", "~> 2.5"
11
+ gem "debug", ">= 1.9"
12
+ gem "rspec", "~> 3.13"
13
+ gem "rubocop", "~> 1.86"
14
+ gem "rubocop-rake", "~> 0.7"
15
+ gem "rubocop-rspec", "~> 3.0"
16
+ end
17
+
18
+ gemspec path: "../"
@@ -0,0 +1,18 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 13.0"
6
+ gem "activemodel", "~> 8.0.0"
7
+ gem "activesupport", "~> 8.0.0"
8
+
9
+ group :development, :test do
10
+ gem "appraisal", "~> 2.5"
11
+ gem "debug", ">= 1.9"
12
+ gem "rspec", "~> 3.13"
13
+ gem "rubocop", "~> 1.86"
14
+ gem "rubocop-rake", "~> 0.7"
15
+ gem "rubocop-rspec", "~> 3.0"
16
+ end
17
+
18
+ gemspec path: "../"
@@ -0,0 +1,18 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 13.0"
6
+ gem "activemodel", "~> 8.1.0"
7
+ gem "activesupport", "~> 8.1.0"
8
+
9
+ group :development, :test do
10
+ gem "appraisal", "~> 2.5"
11
+ gem "debug", ">= 1.9"
12
+ gem "rspec", "~> 3.13"
13
+ gem "rubocop", "~> 1.86"
14
+ gem "rubocop-rake", "~> 0.7"
15
+ gem "rubocop-rspec", "~> 3.0"
16
+ end
17
+
18
+ gemspec path: "../"