simple_ruby_service 1.0.2 → 1.0.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/CHANGELOG.md +9 -1
 - data/README.md +130 -80
 - data/lib/simple_ruby_service/service.rb +7 -4
 - data/lib/simple_ruby_service/version.rb +1 -1
 - data/simple_ruby_service.gemspec +3 -2
 - metadata +9 -8
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 93055c8359bf7677e53e99e5cae18f128e17f643770b51e207cdfd4892bf1ac2
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 916d16d46e8c43ca5815e40b940df3d768e9d22fec56990fa29fb4c0610235a7
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: d68822b1a43f2a664c9e4b6f48a7b0a81de97686d346147e5b1d9bc6163e69d3df83a199908a82da6f2f694b704821aab54c019e35f35f3bfefed47c71499f42
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: cc1b27bbb4d6fd18dfb161d504787a4828a27a0babe4550f0864f1f9f9d740749a0e7983792b8e92d9f030ad7b1afa23be0220ee239939e4cf7cda3b11bb7d96
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -1,8 +1,16 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # Changelog
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            ## 1.0.5 (22-Dec-21)
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            * Added setting to control automatically setting self.value with result of service methods
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            ## 1.0.4 (02-Jul-21)
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            * Polished README (again)
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
       3 
11 
     | 
    
         
             
            ## 1.0.3 (01-Jul-21)
         
     | 
| 
       4 
12 
     | 
    
         | 
| 
       5 
     | 
    
         
            -
            *  
     | 
| 
      
 13 
     | 
    
         
            +
            * Polished README
         
     | 
| 
       6 
14 
     | 
    
         | 
| 
       7 
15 
     | 
    
         
             
            ## 1.0.2 (01-Jul-21)
         
     | 
| 
       8 
16 
     | 
    
         | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -3,13 +3,31 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            [](https://travis-ci.com/amazing-jay/simple_ruby_service)
         
     | 
| 
       4 
4 
     | 
    
         
             
            [](https://codecov.io/gh/amazing-jay/simple_ruby_service)
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
     | 
    
         
            -
            Simple Ruby Service is a lightweight framework for  
     | 
| 
      
 6 
     | 
    
         
            +
            Simple Ruby Service is a lightweight framework for creating Services and Service Objects (SOs) in Ruby.
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
            The framework  
     | 
| 
      
 8 
     | 
    
         
            +
            The framework makes Services and SOs look and feel like ActiveModels, complete with:
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
            1.  
     | 
| 
       11 
     | 
    
         
            -
            2.  
     | 
| 
       12 
     | 
    
         
            -
            3.  
     | 
| 
      
 10 
     | 
    
         
            +
            1. Validations and robust error handling
         
     | 
| 
      
 11 
     | 
    
         
            +
            2. Workflows and method chaining
         
     | 
| 
      
 12 
     | 
    
         
            +
            3. Consistent interfaces
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            Additionally, Simple Ruby Service Objects can stand in for Procs, wherever Procs are expected (via ducktyping).
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            #### What problem does Simple Ruby Service solve?
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            Currently, most ruby developers roll their own services from scratch. As a result, most services are hastely built (in isolation), and this leads to inconsistant interfaces that are difficult to read. Also, error handling tends to vary wildly within an application, and support code tends to be implemented over and over again.
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            Simple Ruby Service addresses these problems and encourages succinct, idiomatic coding styles.
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            #### Should I be using Services & SOs in Ruby / Rails?
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            [LMGTFY](https://www.google.com/search?q=service+object+pattern+rails&rlz=1C5CHFA_enUS893US893&oq=service+object+pattern+rails) to learn more about Services & SOs.
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            **TLDR** - Fat models and fat controllers are bad! Services and Service Objects help you DRY things up.
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
            #### How is a Service different from an SO?
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            An SO is just a Service that encapsulates a single operation (i.e. **one, and only one, responsibility**).
         
     | 
| 
       13 
31 
     | 
    
         | 
| 
       14 
32 
     | 
    
         
             
            ## Requirements
         
     | 
| 
       15 
33 
     | 
    
         | 
| 
         @@ -37,10 +55,12 @@ Source code can be downloaded on GitHub 
     | 
|
| 
       37 
55 
     | 
    
         
             
              [github.com/amazing-jay/simple_ruby_service/tree/master](https://github.com/amazing-jay/simple_ruby_service/tree/master)
         
     | 
| 
       38 
56 
     | 
    
         | 
| 
       39 
57 
     | 
    
         | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
      
 58 
     | 
    
         
            +
            ## Quick Start
         
     | 
| 
       41 
59 
     | 
    
         | 
| 
       42 
60 
     | 
    
         
             
            See [Usage](https://github.com/amazing-jay/simple_ruby_service#usage) & [Creating Simple Ruby Services](https://github.com/amazing-jay/simple_ruby_service#creating-simple-ruby-services) for more information.
         
     | 
| 
       43 
61 
     | 
    
         | 
| 
      
 62 
     | 
    
         
            +
            ### How to refactor complex business logic with Simple Ruby Service
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
       44 
64 
     | 
    
         
             
            #### ::Before:: Vanilla Rails with a fat controller (a contrived example)
         
     | 
| 
       45 
65 
     | 
    
         
             
            ```ruby
         
     | 
| 
       46 
66 
     | 
    
         
             
            # in app/controllers/some_controller.rb
         
     | 
| 
         @@ -51,56 +71,85 @@ class SomeController < ApplicationController 
     | 
|
| 
       51 
71 
     | 
    
         
             
                authorize! resource
         
     | 
| 
       52 
72 
     | 
    
         
             
                resource.do_something
         
     | 
| 
       53 
73 
     | 
    
         
             
                value = resource.do_something_related
         
     | 
| 
      
 74 
     | 
    
         
            +
                raise unless resource.errors
         
     | 
| 
       54 
75 
     | 
    
         
             
                render value
         
     | 
| 
       55 
76 
     | 
    
         
             
              end
         
     | 
| 
       56 
77 
     | 
    
         
             
            end
         
     | 
| 
       57 
78 
     | 
    
         
             
            ```
         
     | 
| 
       58 
79 
     | 
    
         | 
| 
       59 
     | 
    
         
            -
            #### ::After:: Refactored using an  
     | 
| 
      
 80 
     | 
    
         
            +
            #### ::After:: Refactored using an Simple Ruby Service Object
         
     | 
| 
      
 81 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 82 
     | 
    
         
            +
            # in app/controllers/some_controller.rb
         
     | 
| 
      
 83 
     | 
    
         
            +
            class SomeController < ApplicationController
         
     | 
| 
      
 84 
     | 
    
         
            +
              def show
         
     | 
| 
      
 85 
     | 
    
         
            +
                # NOTE: That's right... just one, readable line of code
         
     | 
| 
      
 86 
     | 
    
         
            +
                render DoSomething.call!(params)
         
     | 
| 
      
 87 
     | 
    
         
            +
              end
         
     | 
| 
      
 88 
     | 
    
         
            +
            end
         
     | 
| 
      
 89 
     | 
    
         
            +
            ```
         
     | 
| 
      
 90 
     | 
    
         
            +
             
         
     | 
| 
      
 91 
     | 
    
         
            +
            #### ::Alternate After:: Refactored using a Simple Ruby Service
         
     | 
| 
       60 
92 
     | 
    
         
             
            ```ruby
         
     | 
| 
       61 
93 
     | 
    
         
             
            # in app/controllers/some_controller.rb
         
     | 
| 
       62 
94 
     | 
    
         
             
            class SomeController < ApplicationController
         
     | 
| 
       63 
95 
     | 
    
         
             
              def show
         
     | 
| 
       64 
     | 
    
         
            -
                # NOTE: Simple Ruby Service  
     | 
| 
       65 
     | 
    
         
            -
                render  
     | 
| 
      
 96 
     | 
    
         
            +
                # NOTE: Simple Ruby Service methods can be chained together
         
     | 
| 
      
 97 
     | 
    
         
            +
                render SomeService.new(params)
         
     | 
| 
      
 98 
     | 
    
         
            +
                  .do_something
         
     | 
| 
      
 99 
     | 
    
         
            +
                  .do_something_related
         
     | 
| 
      
 100 
     | 
    
         
            +
                  .value
         
     | 
| 
       66 
101 
     | 
    
         
             
              end
         
     | 
| 
       67 
102 
     | 
    
         
             
            end
         
     | 
| 
      
 103 
     | 
    
         
            +
            ```
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
            ### Taking a peek under the hood
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
            `DoSomething.call!(params)` is deliberately designed to look and feel like `ActiveRecord::Base#save!`.
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
            The following (simplified) implementation illustrates what happens under the hood:
         
     | 
| 
       68 
110 
     | 
    
         | 
| 
      
 111 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 112 
     | 
    
         
            +
            module SimpleRubyService::Object
         
     | 
| 
      
 113 
     | 
    
         
            +
              def self.call!(params)
         
     | 
| 
      
 114 
     | 
    
         
            +
                instance = new(params)
         
     | 
| 
      
 115 
     | 
    
         
            +
                raise Invalid unless instance.valid?
         
     | 
| 
      
 116 
     | 
    
         
            +
                self.value = instance.call
         
     | 
| 
      
 117 
     | 
    
         
            +
                raise Invalid unless instance.failed?
         
     | 
| 
      
 118 
     | 
    
         
            +
                value
         
     | 
| 
      
 119 
     | 
    
         
            +
              end
         
     | 
| 
      
 120 
     | 
    
         
            +
            end
         
     | 
| 
      
 121 
     | 
    
         
            +
            ```
         
     | 
| 
       69 
122 
     | 
    
         | 
| 
      
 123 
     | 
    
         
            +
            ### Anatomy of a Simple Ruby Service Object
         
     | 
| 
      
 124 
     | 
    
         
            +
            ```ruby
         
     | 
| 
       70 
125 
     | 
    
         
             
            # in app/service_objects/do_something.rb
         
     | 
| 
       71 
126 
     | 
    
         
             
            class DoSomething
         
     | 
| 
       72 
127 
     | 
    
         
             
              include SimpleRubyService::ServiceObject
         
     | 
| 
       73 
128 
     | 
    
         | 
| 
      
 129 
     | 
    
         
            +
              # `attribute` behaves similar to ActiveRecord::Base#attribute, but is not typed, or bound to persistant storage
         
     | 
| 
       74 
130 
     | 
    
         
             
              attribute :id
         
     | 
| 
       75 
131 
     | 
    
         
             
              attr_accessor :resource
         
     | 
| 
       76 
132 
     | 
    
         | 
| 
       77 
     | 
    
         
            -
              #  
     | 
| 
      
 133 
     | 
    
         
            +
              # Validations are executed prior to the business logic encapsulated in `perform`
         
     | 
| 
       78 
134 
     | 
    
         
             
              validate do                                 
         
     | 
| 
       79 
135 
     | 
    
         
             
                @resource ||= SomeModel.find(id)
         
     | 
| 
       80 
136 
     | 
    
         
             
                authorize! resource
         
     | 
| 
       81 
137 
     | 
    
         
             
              end
         
     | 
| 
       82 
138 
     | 
    
         | 
| 
       83 
     | 
    
         
            -
              #  
     | 
| 
      
 139 
     | 
    
         
            +
              # The result of `perform` is automatically stored as the SO's `value`
         
     | 
| 
       84 
140 
     | 
    
         
             
              def perform
         
     | 
| 
       85 
     | 
    
         
            -
                resource.do_something
         
     | 
| 
       86 
     | 
    
         
            -
                resource.do_something_related
         
     | 
| 
      
 141 
     | 
    
         
            +
                resource.do_something    
         
     | 
| 
      
 142 
     | 
    
         
            +
                result = resource.do_something_related  
         
     | 
| 
      
 143 
     | 
    
         
            +
                
         
     | 
| 
      
 144 
     | 
    
         
            +
                # Adding any kind of error indicates failure
         
     | 
| 
      
 145 
     | 
    
         
            +
                add_errors_from_object resource    
         
     | 
| 
      
 146 
     | 
    
         
            +
                result
         
     | 
| 
       87 
147 
     | 
    
         
             
              end
         
     | 
| 
       88 
148 
     | 
    
         
             
            end
         
     | 
| 
       89 
149 
     | 
    
         
             
            ```
         
     | 
| 
       90 
150 
     | 
    
         | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
      
 151 
     | 
    
         
            +
            ### Anatomy of a Simple Ruby Service
         
     | 
| 
       92 
152 
     | 
    
         
             
            ```ruby
         
     | 
| 
       93 
     | 
    
         
            -
            # in app/controllers/some_controller.rb
         
     | 
| 
       94 
     | 
    
         
            -
            class SomeController < ApplicationController
         
     | 
| 
       95 
     | 
    
         
            -
              def show
         
     | 
| 
       96 
     | 
    
         
            -
                # NOTE: Simple Ruby Service methods can be chained together
         
     | 
| 
       97 
     | 
    
         
            -
                render SomeService.new(params)
         
     | 
| 
       98 
     | 
    
         
            -
                  .do_something
         
     | 
| 
       99 
     | 
    
         
            -
                  .do_something_related
         
     | 
| 
       100 
     | 
    
         
            -
                  .value
         
     | 
| 
       101 
     | 
    
         
            -
              end
         
     | 
| 
       102 
     | 
    
         
            -
            end
         
     | 
| 
       103 
     | 
    
         
            -
             
     | 
| 
       104 
153 
     | 
    
         
             
            # in app/services/do_something.rb
         
     | 
| 
       105 
154 
     | 
    
         
             
            class SomeService
         
     | 
| 
       106 
155 
     | 
    
         
             
              include SimpleRubyService::Service
         
     | 
| 
         @@ -108,25 +157,38 @@ class SomeService 
     | 
|
| 
       108 
157 
     | 
    
         
             
              attribute :id
         
     | 
| 
       109 
158 
     | 
    
         
             
              attr_accessor :resource
         
     | 
| 
       110 
159 
     | 
    
         | 
| 
       111 
     | 
    
         
            -
              #  
     | 
| 
      
 160 
     | 
    
         
            +
              # Similar to SOs, validations are executed prior to the first service method called
         
     | 
| 
       112 
161 
     | 
    
         
             
              validate do
         
     | 
| 
       113 
162 
     | 
    
         
             
                @resource ||= SomeModel.find(id)
         
     | 
| 
       114 
163 
     | 
    
         
             
                authorize! @resource
         
     | 
| 
       115 
164 
     | 
    
         
             
              end
         
     | 
| 
       116 
165 
     | 
    
         | 
| 
      
 166 
     | 
    
         
            +
              # Unlike SOs, Services can define an arbitrary number of service methods with arbitrary names
         
     | 
| 
       117 
167 
     | 
    
         
             
              service_methods do
         
     | 
| 
       118 
168 
     | 
    
         
             
                def do_something
         
     | 
| 
       119 
     | 
    
         
            -
                  resource. 
     | 
| 
      
 169 
     | 
    
         
            +
                  resource.do_something      
         
     | 
| 
       120 
170 
     | 
    
         
             
                end
         
     | 
| 
       121 
171 
     | 
    
         | 
| 
       122 
     | 
    
         
            -
                #  
     | 
| 
      
 172 
     | 
    
         
            +
                # Unlike SOs, `value` must be explicitely set for Service methods
         
     | 
| 
       123 
173 
     | 
    
         
             
                def do_something_related
         
     | 
| 
       124 
174 
     | 
    
         
             
                  self.value ||= resource.tap &:do_something_related
         
     | 
| 
      
 175 
     | 
    
         
            +
                  add_errors_from_object resource
         
     | 
| 
       125 
176 
     | 
    
         
             
                end
         
     | 
| 
       126 
177 
     | 
    
         
             
              end
         
     | 
| 
       127 
178 
     | 
    
         
             
            end
         
     | 
| 
       128 
179 
     | 
    
         
             
            ```
         
     | 
| 
       129 
180 
     | 
    
         | 
| 
      
 181 
     | 
    
         
            +
            ## A special note about Simple Ruby Service Objects, Procs, and Ducktyping
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
            Simple Ruby Service Objects respond to (`#call`) so they can stand in for Procs, i.e.:
         
     | 
| 
      
 184 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 185 
     | 
    
         
            +
            # in app/models/some_model.rb
         
     | 
| 
      
 186 
     | 
    
         
            +
            class SomeModel < ApplicationRecord
         
     | 
| 
      
 187 
     | 
    
         
            +
              validates :some_attribute, if: SomeServiceObject
         
     | 
| 
      
 188 
     | 
    
         
            +
              [...]
         
     | 
| 
      
 189 
     | 
    
         
            +
            ```
         
     | 
| 
      
 190 
     | 
    
         
            +
            _See [To bang!, or not to bang](https://github.com/amazing-jay/simple_ruby_service/tree/master#to-bang-or-not-to-bang) to learn about `.call!` vs. `.call`._
         
     | 
| 
      
 191 
     | 
    
         
            +
             
     | 
| 
       130 
192 
     | 
    
         
             
            ## Usage
         
     | 
| 
       131 
193 
     | 
    
         | 
| 
       132 
194 
     | 
    
         
             
            ### Service Objects
         
     | 
| 
         @@ -137,8 +199,6 @@ Service Object names should begin with a verb and should not include the words ` 
     | 
|
| 
       137 
199 
     | 
    
         | 
| 
       138 
200 
     | 
    
         
             
            Also, only one operation should be made public, it should always be named `call`, and it should not accept arguments (except for an optional block).
         
     | 
| 
       139 
201 
     | 
    
         | 
| 
       140 
     | 
    
         
            -
            _See [To bang!, or not to bang](https://github.com/amazing-jay/simple_ruby_service/tree/master#to-bang-or-not-to-bang) to learn about `.call!` vs. `.call`._
         
     | 
| 
       141 
     | 
    
         
            -
             
     | 
| 
       142 
202 
     | 
    
         
             
            #### Short form (_recommended_)
         
     | 
| 
       143 
203 
     | 
    
         | 
| 
       144 
204 
     | 
    
         
             
            ```ruby
         
     | 
| 
         @@ -197,9 +257,6 @@ Unlike Service Objects, Service class names should begin with a noun (and may in 
     | 
|
| 
       197 
257 
     | 
    
         | 
| 
       198 
258 
     | 
    
         
             
            Also, any number of operations may be made public, any of these operations may be named `call`, and any of these operations may accept arguments.
         
     | 
| 
       199 
259 
     | 
    
         | 
| 
       200 
     | 
    
         
            -
            _See [To bang!, or not to bang](https://github.com/amazing-jay/simple_ruby_service/tree/master#to-bang-or-not-to-bang) to learn about `.service_method_name!` vs. `.service_method_name`._
         
     | 
| 
       201 
     | 
    
         
            -
             
     | 
| 
       202 
     | 
    
         
            -
             
     | 
| 
       203 
260 
     | 
    
         
             
            #### Short form
         
     | 
| 
       204 
261 
     | 
    
         | 
| 
       205 
262 
     | 
    
         
             
            _not available for Services_
         
     | 
| 
         @@ -254,7 +311,7 @@ end 
     | 
|
| 
       254 
311 
     | 
    
         
             
            ## Creating Simple Ruby Services
         
     | 
| 
       255 
312 
     | 
    
         | 
| 
       256 
313 
     | 
    
         
             
            ### Service Objects
         
     | 
| 
       257 
     | 
    
         
            -
            To implement  
     | 
| 
      
 314 
     | 
    
         
            +
            To implement a Simple Ruby Service Object:
         
     | 
| 
       258 
315 
     | 
    
         | 
| 
       259 
316 
     | 
    
         
             
              1. include `SimpleRubyService::ServiceObject`
         
     | 
| 
       260 
317 
     | 
    
         
             
              2. declare attributes with the `attribute` keyword (class level DSL)
         
     | 
| 
         @@ -282,7 +339,7 @@ end 
     | 
|
| 
       282 
339 
     | 
    
         
             
            ```
         
     | 
| 
       283 
340 
     | 
    
         | 
| 
       284 
341 
     | 
    
         
             
            ### Services
         
     | 
| 
       285 
     | 
    
         
            -
            To implement  
     | 
| 
      
 342 
     | 
    
         
            +
            To implement a Simple Ruby Service:
         
     | 
| 
       286 
343 
     | 
    
         | 
| 
       287 
344 
     | 
    
         
             
              1. include `SimpleRubyService::Service`
         
     | 
| 
       288 
345 
     | 
    
         
             
              2. declare attributes with the `attribute` keyword (class level DSL)
         
     | 
| 
         @@ -319,19 +376,46 @@ class SomeService 
     | 
|
| 
       319 
376 
     | 
    
         
             
            end
         
     | 
| 
       320 
377 
     | 
    
         
             
            ```
         
     | 
| 
       321 
378 
     | 
    
         | 
| 
       322 
     | 
    
         
            -
             
     | 
| 
      
 379 
     | 
    
         
            +
            ### Workflows
         
     | 
| 
      
 380 
     | 
    
         
            +
            Simple Ruby Services are inherently a good fit for workflows because they support chaining, i.e.:
         
     | 
| 
       323 
381 
     | 
    
         | 
| 
       324 
     | 
    
         
            -
             
     | 
| 
      
 382 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 383 
     | 
    
         
            +
            SomeService.new(params)
         
     | 
| 
      
 384 
     | 
    
         
            +
              .do_something
         
     | 
| 
      
 385 
     | 
    
         
            +
              .do_something_related
         
     | 
| 
      
 386 
     | 
    
         
            +
              .value
         
     | 
| 
      
 387 
     | 
    
         
            +
            ```
         
     | 
| 
       325 
388 
     | 
    
         | 
| 
       326 
     | 
    
         
            -
             
     | 
| 
      
 389 
     | 
    
         
            +
            But SOs can also implement various workflows with dependency injection:
         
     | 
| 
       327 
390 
     | 
    
         | 
| 
       328 
     | 
    
         
            -
             
     | 
| 
      
 391 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 392 
     | 
    
         
            +
            class PerformSomeWorkflow < SimpleRubyService::ServiceObject
         
     | 
| 
      
 393 
     | 
    
         
            +
              def perform
         
     | 
| 
      
 394 
     | 
    
         
            +
                dependency = SimpleRubyService1.call!
         
     | 
| 
      
 395 
     | 
    
         
            +
                result = SimpleRubyService2.call(dependency)
         
     | 
| 
      
 396 
     | 
    
         
            +
                raise unless result.success?                 
         
     | 
| 
      
 397 
     | 
    
         
            +
                SimpleRubyService3(dependency, result.value).call!
         
     | 
| 
      
 398 
     | 
    
         
            +
              end
         
     | 
| 
      
 399 
     | 
    
         
            +
            end
         
     | 
| 
      
 400 
     | 
    
         
            +
            ```
         
     | 
| 
       329 
401 
     | 
    
         | 
| 
       330 
     | 
    
         
            -
             
     | 
| 
      
 402 
     | 
    
         
            +
            ## MISC
         
     | 
| 
       331 
403 
     | 
    
         | 
| 
       332 
     | 
    
         
            -
             
     | 
| 
      
 404 
     | 
    
         
            +
            ### To bang!, or not to bang
         
     | 
| 
      
 405 
     | 
    
         
            +
             
     | 
| 
      
 406 
     | 
    
         
            +
            Use the bang! version of an operation whenever you expect the operation to succeed more often than fail, and you don't need to chain operations together.
         
     | 
| 
      
 407 
     | 
    
         
            +
             
     | 
| 
      
 408 
     | 
    
         
            +
            Similar in pattern to `ActiveRecord#save!`, the bang version of each operation:
         
     | 
| 
      
 409 
     | 
    
         
            +
            * raises `SimpleRubyService::Invalid` if `valid?` is falsey
         
     | 
| 
      
 410 
     | 
    
         
            +
            * raises `SimpleRubyService::Failure` if the block provided returns a falsey value
         
     | 
| 
      
 411 
     | 
    
         
            +
            * returns `@value`
         
     | 
| 
       333 
412 
     | 
    
         | 
| 
       334 
     | 
    
         
            -
             
     | 
| 
      
 413 
     | 
    
         
            +
            Whereas, similar in pattern to `ActiveRecord#save`, the regular version of each operation:
         
     | 
| 
      
 414 
     | 
    
         
            +
            * doesn't raise any exceptions
         
     | 
| 
      
 415 
     | 
    
         
            +
            * passes the return value of the block provided to `#success?` 
         
     | 
| 
      
 416 
     | 
    
         
            +
            * returns self << _note: this is unlike `ActiveRecord#save`_
         
     | 
| 
      
 417 
     | 
    
         
            +
             
     | 
| 
      
 418 
     | 
    
         
            +
            ### Service or SO?
         
     | 
| 
       335 
419 
     | 
    
         | 
| 
       336 
420 
     | 
    
         
             
            Use a `Service` when encapsulating related operations that share dependencies & validations.
         
     | 
| 
       337 
421 
     | 
    
         | 
| 
         @@ -342,9 +426,6 @@ i.e.: 
     | 
|
| 
       342 
426 
     | 
    
         | 
| 
       343 
427 
     | 
    
         
             
            _note: Things get fuzzy when operations share some, but not all, dependencies & validations. Use your best judgement when operation `A` and operation `B` are related but `A` acts on a `User` while `B` acts on both a `User` & a `Company`._
         
     | 
| 
       344 
428 
     | 
    
         | 
| 
       345 
     | 
    
         
            -
            ### Atomicity
         
     | 
| 
       346 
     | 
    
         
            -
            The framework does not include transaction support by default. You are responsible for wrapping with a transaction if atomicity is desired.
         
     | 
| 
       347 
     | 
    
         
            -
             
     | 
| 
       348 
429 
     | 
    
         
             
            ### Control Flow
         
     | 
| 
       349 
430 
     | 
    
         
             
            Rescue exceptions that represent internal control flow and propogate the rest.
         
     | 
| 
       350 
431 
     | 
    
         | 
| 
         @@ -352,7 +433,7 @@ For example, if an internal call to User.create! is expected to always succeed, 
     | 
|
| 
       352 
433 
     | 
    
         | 
| 
       353 
434 
     | 
    
         
             
            Example::
         
     | 
| 
       354 
435 
     | 
    
         
             
            ```ruby
         
     | 
| 
       355 
     | 
    
         
            -
            class DoSomethingDangerous < SimpleRubyService:: 
     | 
| 
      
 436 
     | 
    
         
            +
            class DoSomethingDangerous < SimpleRubyService::ServiceObject
         
     | 
| 
       356 
437 
     | 
    
         
             
              attribute :attr1, :attr2             # should include all params required to execute
         
     | 
| 
       357 
438 
     | 
    
         
             
              validates_presence_of :attr1         # validate params to call
         
     | 
| 
       358 
439 
     | 
    
         | 
| 
         @@ -368,38 +449,6 @@ class DoSomethingDangerous < SimpleRubyService::ObjectBase 
     | 
|
| 
       368 
449 
     | 
    
         
             
            end
         
     | 
| 
       369 
450 
     | 
    
         
             
            ```
         
     | 
| 
       370 
451 
     | 
    
         | 
| 
       371 
     | 
    
         
            -
            ## Workflows
         
     | 
| 
       372 
     | 
    
         
            -
            SOs often need to call other SOs in order to implement various workflows:
         
     | 
| 
       373 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       374 
     | 
    
         
            -
            class PerformSomeWorkflow < SimpleRubyService::ObjectBase
         
     | 
| 
       375 
     | 
    
         
            -
              def perform
         
     | 
| 
       376 
     | 
    
         
            -
                dependency = SimpleRubyService1.call!
         
     | 
| 
       377 
     | 
    
         
            -
                result = SimpleRubyService2.call(dependency)
         
     | 
| 
       378 
     | 
    
         
            -
                raise unless result.success?                 
         
     | 
| 
       379 
     | 
    
         
            -
                SimpleRubyService3(dependency, result.value).call!
         
     | 
| 
       380 
     | 
    
         
            -
              end
         
     | 
| 
       381 
     | 
    
         
            -
            end
         
     | 
| 
       382 
     | 
    
         
            -
            ```
         
     | 
| 
       383 
     | 
    
         
            -
             
     | 
| 
       384 
     | 
    
         
            -
            ## MISC
         
     | 
| 
       385 
     | 
    
         
            -
             
     | 
| 
       386 
     | 
    
         
            -
            ### Attributes
         
     | 
| 
       387 
     | 
    
         
            -
            The `attribute` and `attributes` keywords behaves similar to [ActiveRecord::Base.attribute](https://api.rubyonrails.org/v6.1.3.1/classes/ActiveRecord/Attributes/ClassMethods.html), but they are not typed or bound to persistant storage.
         
     | 
| 
       388 
     | 
    
         
            -
             
     | 
| 
       389 
     | 
    
         
            -
            ### To bang!, or not to bang
         
     | 
| 
       390 
     | 
    
         
            -
             
     | 
| 
       391 
     | 
    
         
            -
            Use the bang! version of an operation whenever you expect the operation to succeed more often than fail, and you don't need to chain operations together.
         
     | 
| 
       392 
     | 
    
         
            -
             
     | 
| 
       393 
     | 
    
         
            -
            Similar in pattern to `ActiveRecord#save!`, the bang version of each operation:
         
     | 
| 
       394 
     | 
    
         
            -
            * raises `SimpleRubyService::Invalid` if `valid?` is falsey
         
     | 
| 
       395 
     | 
    
         
            -
            * raises `SimpleRubyService::Failure` if the block provided returns a falsey value
         
     | 
| 
       396 
     | 
    
         
            -
            * returns `@value`
         
     | 
| 
       397 
     | 
    
         
            -
             
     | 
| 
       398 
     | 
    
         
            -
            Whereas, similar in pattern to `ActiveRecord#save`, the regular version of each operation:
         
     | 
| 
       399 
     | 
    
         
            -
            * doesn't raise any exceptions
         
     | 
| 
       400 
     | 
    
         
            -
            * passes the return value of the block provided to `#success?` 
         
     | 
| 
       401 
     | 
    
         
            -
            * returns self << _note: this is unlike `ActiveRecord#save`_
         
     | 
| 
       402 
     | 
    
         
            -
             
     | 
| 
       403 
452 
     | 
    
         
             
            ## Development
         
     | 
| 
       404 
453 
     | 
    
         | 
| 
       405 
454 
     | 
    
         
             
            After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
         
     | 
| 
         @@ -416,6 +465,7 @@ The gem is available as open source under the terms of the [MIT License](https:/ 
     | 
|
| 
       416 
465 
     | 
    
         | 
| 
       417 
466 
     | 
    
         
             
            ## DEVELOPMENT ROADMAP
         
     | 
| 
       418 
467 
     | 
    
         | 
| 
       419 
     | 
    
         
            -
            1. Create a  
     | 
| 
       420 
     | 
    
         
            -
            2.  
     | 
| 
      
 468 
     | 
    
         
            +
            1. Create a class level DSL to stop before each Service method unless errors.empty?
         
     | 
| 
      
 469 
     | 
    
         
            +
            2. Create a helper to dynamically generate default SOs for ActiveRecord models (`create`, `update`, and `destroy`) _(when used in a project that includes [ActiveRecord](https://github.com/rails/rails/tree/main/activerecord))_.
         
     | 
| 
      
 470 
     | 
    
         
            +
            3. Consider isolating validation errors from execution errors (so that invalid? is not always true when failed? is true)
         
     | 
| 
       421 
471 
     | 
    
         | 
| 
         @@ -6,9 +6,13 @@ module SimpleRubyService 
     | 
|
| 
       6 
6 
     | 
    
         
             
                extend ActiveSupport::Concern
         
     | 
| 
       7 
7 
     | 
    
         
             
                include ActiveModel::AttributeAssignment
         
     | 
| 
       8 
8 
     | 
    
         
             
                include ActiveModel::Validations
         
     | 
| 
      
 9 
     | 
    
         
            +
                include ActiveModel::Validations::Callbacks
         
     | 
| 
       9 
10 
     | 
    
         | 
| 
       10 
11 
     | 
    
         
             
                included do
         
     | 
| 
       11 
12 
     | 
    
         
             
                  attr_accessor :value
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  class_attribute :set_value_when_service_methods_return
         
     | 
| 
      
 15 
     | 
    
         
            +
                  self.set_value_when_service_methods_return = true
         
     | 
| 
       12 
16 
     | 
    
         
             
                end
         
     | 
| 
       13 
17 
     | 
    
         | 
| 
       14 
18 
     | 
    
         
             
                class_methods do
         
     | 
| 
         @@ -32,7 +36,7 @@ module SimpleRubyService 
     | 
|
| 
       32 
36 
     | 
    
         
             
                  # Class level DSL that wraps the methods defined in inherited classes.
         
     | 
| 
       33 
37 
     | 
    
         
             
                  def service_methods(&blk)
         
     | 
| 
       34 
38 
     | 
    
         
             
                    Module.new.tap do |m| # Using anonymous modules so that super can be used to extend service methods
         
     | 
| 
       35 
     | 
    
         
            -
                      m.module_eval 
     | 
| 
      
 39 
     | 
    
         
            +
                      m.module_eval(&blk)
         
     | 
| 
       36 
40 
     | 
    
         
             
                      include m
         
     | 
| 
       37 
41 
     | 
    
         | 
| 
       38 
42 
     | 
    
         
             
                      m.instance_methods.each do |service_method|
         
     | 
| 
         @@ -41,7 +45,8 @@ module SimpleRubyService 
     | 
|
| 
       41 
45 
     | 
    
         
             
                        # Returns self (for chainability).
         
     | 
| 
       42 
46 
     | 
    
         
             
                        # Evaluates validity prior to executing the block provided.
         
     | 
| 
       43 
47 
     | 
    
         
             
                        define_method service_method do |*args, **kwargs, &callback|
         
     | 
| 
       44 
     | 
    
         
            -
                          perform(service_method, *args, **kwargs, &callback) if valid?
         
     | 
| 
      
 48 
     | 
    
         
            +
                          result = perform(service_method, *args, **kwargs, &callback) if valid?
         
     | 
| 
      
 49 
     | 
    
         
            +
                          self.value = result if set_value_when_service_methods_return
         
     | 
| 
       45 
50 
     | 
    
         | 
| 
       46 
51 
     | 
    
         
             
                          self
         
     | 
| 
       47 
52 
     | 
    
         
             
                        end
         
     | 
| 
         @@ -130,5 +135,3 @@ module SimpleRubyService 
     | 
|
| 
       130 
135 
     | 
    
         
             
                end
         
     | 
| 
       131 
136 
     | 
    
         
             
              end
         
     | 
| 
       132 
137 
     | 
    
         
             
            end
         
     | 
| 
       133 
     | 
    
         
            -
             
     | 
| 
       134 
     | 
    
         
            -
             
     | 
    
        data/simple_ruby_service.gemspec
    CHANGED
    
    | 
         @@ -10,8 +10,9 @@ Gem::Specification.new do |spec| 
     | 
|
| 
       10 
10 
     | 
    
         
             
              spec.authors       = ["Jay Crouch"]
         
     | 
| 
       11 
11 
     | 
    
         
             
              spec.email         = ["i.jaycrouch@gmail.com"]
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
     | 
    
         
            -
              spec.summary       = 'Simple Ruby Service is a lightweight framework for  
     | 
| 
       14 
     | 
    
         
            -
              spec.description   = 'Simple Ruby Service is a lightweight framework for  
     | 
| 
      
 13 
     | 
    
         
            +
              spec.summary       = 'Simple Ruby Service is a lightweight framework for creating Services and Service Objects (SOs) in Ruby.'
         
     | 
| 
      
 14 
     | 
    
         
            +
              spec.description   = 'Simple Ruby Service is a lightweight framework for creating Services and Service Objects (SOs) in Ruby. ' \
         
     | 
| 
      
 15 
     | 
    
         
            +
                                   'The framework makes Services and SOs look and feel like ActiveModels, complete with: 1. validations and robust error handling; 2. workflows and method chaining; and 3. consistent interfaces. Additionally, Simple Ruby Service Objects can stand in for Procs, wherever Procs are expected (via ducktyping).'
         
     | 
| 
       15 
16 
     | 
    
         
             
              spec.homepage      = 'https://github.com/amazing-jay/simple_ruby_service'
         
     | 
| 
       16 
17 
     | 
    
         
             
              spec.license       = "MIT"
         
     | 
| 
       17 
18 
     | 
    
         | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: simple_ruby_service
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 1.0. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 1.0.6
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Jay Crouch
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire:
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: exe
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date: 2021- 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2021-12-22 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: activemodel
         
     | 
| 
         @@ -304,10 +304,11 @@ dependencies: 
     | 
|
| 
       304 
304 
     | 
    
         
             
                - - "~>"
         
     | 
| 
       305 
305 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       306 
306 
     | 
    
         
             
                    version: '3.13'
         
     | 
| 
       307 
     | 
    
         
            -
            description: 'Simple Ruby Service is a lightweight framework for  
     | 
| 
       308 
     | 
    
         
            -
               
     | 
| 
       309 
     | 
    
         
            -
               
     | 
| 
       310 
     | 
    
         
            -
               
     | 
| 
      
 307 
     | 
    
         
            +
            description: 'Simple Ruby Service is a lightweight framework for creating Services
         
     | 
| 
      
 308 
     | 
    
         
            +
              and Service Objects (SOs) in Ruby. The framework makes Services and SOs look and
         
     | 
| 
      
 309 
     | 
    
         
            +
              feel like ActiveModels, complete with: 1. validations and robust error handling;
         
     | 
| 
      
 310 
     | 
    
         
            +
              2. workflows and method chaining; and 3. consistent interfaces. Additionally, Simple
         
     | 
| 
      
 311 
     | 
    
         
            +
              Ruby Service Objects can stand in for Procs, wherever Procs are expected (via ducktyping).'
         
     | 
| 
       311 
312 
     | 
    
         
             
            email:
         
     | 
| 
       312 
313 
     | 
    
         
             
            - i.jaycrouch@gmail.com
         
     | 
| 
       313 
314 
     | 
    
         
             
            executables: []
         
     | 
| 
         @@ -358,6 +359,6 @@ requirements: [] 
     | 
|
| 
       358 
359 
     | 
    
         
             
            rubygems_version: 3.0.9
         
     | 
| 
       359 
360 
     | 
    
         
             
            signing_key:
         
     | 
| 
       360 
361 
     | 
    
         
             
            specification_version: 4
         
     | 
| 
       361 
     | 
    
         
            -
            summary: Simple Ruby Service is a lightweight framework for  
     | 
| 
       362 
     | 
    
         
            -
               
     | 
| 
      
 362 
     | 
    
         
            +
            summary: Simple Ruby Service is a lightweight framework for creating Services and
         
     | 
| 
      
 363 
     | 
    
         
            +
              Service Objects (SOs) in Ruby.
         
     | 
| 
       363 
364 
     | 
    
         
             
            test_files: []
         
     |