light_service_object 0.1.5 → 0.1.6
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/Gemfile.lock +46 -0
- data/README.md +49 -5
- data/bin/rspec +29 -0
- data/lib/light_service_object/version.rb +1 -1
- data/lib/light_service_object.rb +48 -30
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6a4e68067319da840f48462ed29d2fdb32314e14c78c543f6e978b345d2d87b7
|
4
|
+
data.tar.gz: 0fb567f252a98e5bef84d9d2b7db469e86b6b9bdce13038dd701f703c5be474d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: edb266a2036ce57aba328e8c7fe3925b1955accd7537c4cdf7ff8ea14111eaa1234c33c974d611909031869998a648277abff83f4e3431e4391e14443da172f3
|
7
|
+
data.tar.gz: 9e6faf7bd3f116726ab2260401f7d5e9b03c41135c377bf46f37a498ad6c57026928590c147feb442c3b681cbf8c755e4207dbc00b2981869bb70a7ccafb20e0
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
light_service_object (0.1.6)
|
5
|
+
dry-initializer (~> 2.0)
|
6
|
+
dry-monads (~> 1.0)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
concurrent-ruby (1.1.5)
|
12
|
+
diff-lcs (1.3)
|
13
|
+
dry-core (0.4.9)
|
14
|
+
concurrent-ruby (~> 1.0)
|
15
|
+
dry-equalizer (0.2.2)
|
16
|
+
dry-initializer (2.5.0)
|
17
|
+
dry-monads (1.3.1)
|
18
|
+
concurrent-ruby (~> 1.0)
|
19
|
+
dry-core (~> 0.4, >= 0.4.4)
|
20
|
+
dry-equalizer
|
21
|
+
rake (10.5.0)
|
22
|
+
rspec (3.8.0)
|
23
|
+
rspec-core (~> 3.8.0)
|
24
|
+
rspec-expectations (~> 3.8.0)
|
25
|
+
rspec-mocks (~> 3.8.0)
|
26
|
+
rspec-core (3.8.2)
|
27
|
+
rspec-support (~> 3.8.0)
|
28
|
+
rspec-expectations (3.8.4)
|
29
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
30
|
+
rspec-support (~> 3.8.0)
|
31
|
+
rspec-mocks (3.8.1)
|
32
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
33
|
+
rspec-support (~> 3.8.0)
|
34
|
+
rspec-support (3.8.2)
|
35
|
+
|
36
|
+
PLATFORMS
|
37
|
+
ruby
|
38
|
+
|
39
|
+
DEPENDENCIES
|
40
|
+
bundler (~> 2.0)
|
41
|
+
light_service_object!
|
42
|
+
rake (~> 10.0)
|
43
|
+
rspec (~> 3.0)
|
44
|
+
|
45
|
+
BUNDLED WITH
|
46
|
+
2.0.2
|
data/README.md
CHANGED
@@ -14,15 +14,59 @@ And then execute:
|
|
14
14
|
|
15
15
|
$ bundle
|
16
16
|
|
17
|
-
##
|
17
|
+
## A Light Service Object with a Contract
|
18
18
|
|
19
|
-
|
19
|
+
Service objects are a great way to encapsulate business/domain functionality in a Rails app.
|
20
20
|
|
21
|
-
|
21
|
+
### The Old Way
|
22
22
|
|
23
|
-
|
23
|
+
They typically wrap some functionality up in a `call` method, with an initializer for setting parameters.
|
24
|
+
|
25
|
+
```
|
26
|
+
class TypicalServiceObject
|
27
|
+
def initialize(date, number)
|
28
|
+
@date = date
|
29
|
+
@number = number
|
30
|
+
end
|
31
|
+
|
32
|
+
def call
|
33
|
+
@date = Date.parse(@date) if @date.is_a?(String)
|
34
|
+
If @date - Date.today < 7 then
|
35
|
+
@number += 10
|
36
|
+
else
|
37
|
+
raise ArgumentError.new("Date is too far away")
|
38
|
+
end
|
39
|
+
@number
|
40
|
+
end
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
This service object has a few problems:
|
45
|
+
- No indication of what it's "contract" is with the outside world
|
46
|
+
- No way to indicate failure other than Exceptions
|
47
|
+
- Manual conversion of data into the expected form
|
48
|
+
|
49
|
+
### The New Way
|
50
|
+
|
51
|
+
```
|
52
|
+
class NewServiceObject < LightServiceObject::Base
|
53
|
+
required :date, ensure: Date
|
54
|
+
optional :number
|
55
|
+
|
56
|
+
def perform
|
57
|
+
fail!("Date is too far away") if date - Date.today >= 7
|
58
|
+
|
59
|
+
number + 10
|
60
|
+
end
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
- date is required, a failure will be returned with the error message
|
65
|
+
- date will be transformed into a Date if it isn't one already `ensure: Date`
|
66
|
+
- `fail!(message)` causes the service to return a failure and message
|
67
|
+
- the last thing evaluated will be returned as the result `number + 10`
|
68
|
+
- one side note: all parameters are immutable by default
|
24
69
|
|
25
|
-
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 tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
26
70
|
|
27
71
|
## Contributing
|
28
72
|
|
data/bin/rspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rspec' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rspec-core", "rspec")
|
data/lib/light_service_object.rb
CHANGED
@@ -36,37 +36,55 @@ module LightServiceObject
|
|
36
36
|
|
37
37
|
|
38
38
|
## — CLASS METHODS
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
class << self
|
40
|
+
def result_class
|
41
|
+
@result_class
|
42
|
+
end
|
42
43
|
|
43
|
-
|
44
|
-
|
45
|
-
|
44
|
+
def param(key, **options)
|
45
|
+
raise Error.new("Do not use param in a service object")
|
46
|
+
end
|
46
47
|
|
47
|
-
|
48
|
-
|
49
|
-
|
48
|
+
def option(*args, **opts, &block)
|
49
|
+
if opts.delete(:mutable)
|
50
|
+
self.send("attr_writer", args.first)
|
51
|
+
end
|
52
|
+
super(*args, **opts, &block)
|
53
|
+
end
|
50
54
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
+
def required(key, **options)
|
56
|
+
options[:private] = true
|
57
|
+
option(key, **options)
|
58
|
+
end
|
55
59
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
+
def optional(key, **options)
|
61
|
+
options[:optional] = true
|
62
|
+
options[:private] = true
|
63
|
+
option(key, **options)
|
64
|
+
end
|
60
65
|
|
61
|
-
|
62
|
-
|
66
|
+
def expected_result_class(klass)
|
67
|
+
@result_class = klass
|
68
|
+
@result_class = klass.constantize if klass.is_a?(String)
|
69
|
+
end
|
63
70
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
71
|
+
def call(**options)
|
72
|
+
begin
|
73
|
+
obj = self.new(**options)
|
74
|
+
rescue KeyError => e
|
75
|
+
return Dry::Monads.Failure(e.message)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Identify incoming params that weren't specified
|
79
|
+
# set_params = obj.instance_variables.map{|e| e.to_s.tr("@","").to_sym }
|
80
|
+
# unknown_params = (options.keys - set_params)
|
81
|
+
# ap("#{self.name} > Unknown Parameters #{unknown_params}") if unknown_params.present?
|
82
|
+
|
83
|
+
result = obj.call
|
84
|
+
end
|
85
|
+
end
|
68
86
|
|
69
|
-
|
87
|
+
def self.failed(error)
|
70
88
|
end
|
71
89
|
|
72
90
|
## — INSTANCE METHODS
|
@@ -76,7 +94,7 @@ module LightServiceObject
|
|
76
94
|
|
77
95
|
def call
|
78
96
|
result = self.perform
|
79
|
-
if self.result_class
|
97
|
+
if self.result_class
|
80
98
|
if !result.is_a?(self.result_class)
|
81
99
|
a_name = "#{self.result_class}"
|
82
100
|
a_name = %w[a e i o u y].include?(a_name.first.downcase) ? "an #{a_name}" : "a #{a_name}"
|
@@ -86,19 +104,19 @@ module LightServiceObject
|
|
86
104
|
end
|
87
105
|
Dry::Monads.Success(result)
|
88
106
|
rescue StandardError => error
|
89
|
-
|
90
|
-
self.class.failed(error)
|
91
|
-
Dry::Monads.Failure(reason)
|
107
|
+
fail!(error)
|
92
108
|
end
|
93
109
|
|
94
110
|
def fail!(error)
|
95
111
|
error = ::StandardError.new(error.to_s) if !error.is_a?(::StandardError)
|
96
|
-
|
112
|
+
reason = self.error_reason(error)
|
113
|
+
self.class.failed(error)
|
114
|
+
Dry::Monads.Failure(reason)
|
97
115
|
end
|
98
116
|
|
99
117
|
def error_reason(error)
|
100
118
|
# Give subclasses a chance to see errors first
|
101
|
-
"#{self.class}
|
119
|
+
"[#{self.class}] #{error}"
|
102
120
|
end
|
103
121
|
end
|
104
122
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: light_service_object
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Sharpe
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-10-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -92,10 +92,12 @@ files:
|
|
92
92
|
- ".travis.yml"
|
93
93
|
- CHANGELOG.md
|
94
94
|
- Gemfile
|
95
|
+
- Gemfile.lock
|
95
96
|
- LICENSE.txt
|
96
97
|
- README.md
|
97
98
|
- Rakefile
|
98
99
|
- bin/console
|
100
|
+
- bin/rspec
|
99
101
|
- bin/setup
|
100
102
|
- lib/light_service_object.rb
|
101
103
|
- lib/light_service_object/version.rb
|
@@ -119,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
121
|
- !ruby/object:Gem::Version
|
120
122
|
version: '0'
|
121
123
|
requirements: []
|
122
|
-
rubygems_version: 3.0.
|
124
|
+
rubygems_version: 3.0.6
|
123
125
|
signing_key:
|
124
126
|
specification_version: 4
|
125
127
|
summary: A lightweight base service object for Rails/Ruby
|