nifty_services 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +6 -2
- data/README.md +64 -1195
- data/docs/api.md +142 -0
- data/docs/callbacks.md +67 -0
- data/docs/cli.md +13 -0
- data/docs/configuration.md +62 -0
- data/docs/crud_services.md +534 -0
- data/docs/i18n.md +55 -0
- data/docs/services_markup.md +271 -0
- data/docs/usage.md +204 -0
- data/docs/webframeworks_integration.md +145 -0
- data/lib/nifty_services.rb +5 -8
- data/lib/nifty_services/base_action_service.rb +7 -10
- data/lib/nifty_services/base_create_service.rb +35 -45
- data/lib/nifty_services/base_crud_service.rb +18 -43
- data/lib/nifty_services/base_delete_service.rb +22 -21
- data/lib/nifty_services/base_service.rb +52 -42
- data/lib/nifty_services/base_update_service.rb +19 -20
- data/lib/nifty_services/configuration.rb +16 -17
- data/lib/nifty_services/errors.rb +2 -2
- data/lib/nifty_services/extensions/callbacks.rb +173 -0
- data/lib/nifty_services/support/hash.rb +14 -0
- data/lib/nifty_services/support/string.rb +21 -0
- data/lib/nifty_services/util.rb +4 -4
- data/lib/nifty_services/version.rb +1 -1
- data/nifty_services.gemspec +1 -1
- metadata +21 -10
- data/lib/nifty_services/extensions/callbacks_interface.rb +0 -171
data/docs/i18n.md
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# NiftyServices documentation
|
2
|
+
|
3
|
+
---
|
4
|
+
|
5
|
+
## :us: :fr: :jp: I18n Support :uk: :es: :de:
|
6
|
+
|
7
|
+
As you see in the above examples, with `NiftyServices` you can respond in multiples languages for the same service error messages, by default your locales config file must be configured as:
|
8
|
+
|
9
|
+
```yml
|
10
|
+
# attention: dont use `resource_type`
|
11
|
+
# use the key setup up in `record_error_key` methods
|
12
|
+
resource_type:
|
13
|
+
not_found: "Invalid or not found post"
|
14
|
+
cant_create: "Can't delete this record"
|
15
|
+
cant_read: "Can't access this record"
|
16
|
+
cant_update: "Can't delete this record"
|
17
|
+
cant_delete: "Can't delete this record"
|
18
|
+
users:
|
19
|
+
not_found: "Invalid or not found user"
|
20
|
+
```
|
21
|
+
|
22
|
+
You can configure the default I18n namespace using configuration:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
NiftyServies.configure do |config|
|
26
|
+
config.i18n_namespace = :my_app
|
27
|
+
end
|
28
|
+
```
|
29
|
+
|
30
|
+
Example config for `Post` and `Comment` resources using `my_app` locale namespace:
|
31
|
+
|
32
|
+
```yml
|
33
|
+
# default is nifty_services
|
34
|
+
my_app:
|
35
|
+
errors:
|
36
|
+
default_crud: &default_crud
|
37
|
+
cant_create: "Can't delete this record"
|
38
|
+
cant_read: "Can't access this record"
|
39
|
+
cant_update: "Can't delete this record"
|
40
|
+
cant_delete: "Can't delete this record"
|
41
|
+
users:
|
42
|
+
not_found: "Invalid or not found user"
|
43
|
+
posts:
|
44
|
+
<<: *default_crud
|
45
|
+
not_found: "Invalid or not found post"
|
46
|
+
comments:
|
47
|
+
<<: *default_crud
|
48
|
+
not_found: "Invalid or not found comment"
|
49
|
+
```
|
50
|
+
|
51
|
+
---
|
52
|
+
|
53
|
+
### Next
|
54
|
+
|
55
|
+
See [Callbacks](./callbacks.md)
|
@@ -0,0 +1,271 @@
|
|
1
|
+
# NiftyServices documentation
|
2
|
+
|
3
|
+
---
|
4
|
+
|
5
|
+
## :pray: Basic Service Markups :raised_hands:
|
6
|
+
|
7
|
+
Here, for your convenience and sanity all basic service structures for reference when you start a brand new Service.
|
8
|
+
Most of time, the best way is to copy all content from each service described below and change according to your needs.
|
9
|
+
|
10
|
+
### BaseCreateService Basic Markup
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
class SomeCreateService < NiftyServices::BaseCreateService
|
14
|
+
# [Required]
|
15
|
+
# remember that inside the Service you always can use
|
16
|
+
# @record variable to access current record
|
17
|
+
# and from outside (service instance):
|
18
|
+
# service.record or service.record_type
|
19
|
+
# eg:
|
20
|
+
# record_type BlogPost
|
21
|
+
# service.record # BlogPost.new(...)
|
22
|
+
# service.blog_post # BlogPost.new(...)
|
23
|
+
# service.record == service.blog_post # true
|
24
|
+
# alias_name can be used to create a custom alias name
|
25
|
+
# eg:
|
26
|
+
# record_type BlogPost, alias_name: :post
|
27
|
+
# service.record # BlogPost.new(...)
|
28
|
+
# service.post # BlogPost.new(...)
|
29
|
+
# service.record == service.post # true
|
30
|
+
|
31
|
+
record_type RecordType, alias_name: :my_custom_alias_name
|
32
|
+
|
33
|
+
## Its a good convention to create a constant and not returning the plain
|
34
|
+
## array in `record_attributes_whitelist` since this can be accessed from others
|
35
|
+
## places, as: `SomeCreateService::WHITELIST_ATTRIBUTES`
|
36
|
+
WHITELIST_ATTRIBUTES = [
|
37
|
+
:safe_attribute_1,
|
38
|
+
:safe_attribute_2,
|
39
|
+
]
|
40
|
+
|
41
|
+
# [Required]
|
42
|
+
# You must setup whitelist attributes or define the method `record_attributes_whitelist` method
|
43
|
+
whitelist_attributes WHITELIST_ATTRIBUTES
|
44
|
+
|
45
|
+
# You can freely override initialize method to receive more arguments
|
46
|
+
def initialize(record, user, options = {})
|
47
|
+
@user = user
|
48
|
+
super(record, options)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
# [Optional]
|
53
|
+
# this key is used for I18n translations, you don't need to override or implement
|
54
|
+
# NiftyService will try to inflect this using `record_type.to_s.underscore + 's'`
|
55
|
+
|
56
|
+
# So, if your record type is `Post`, `record_error_key` will be `posts`
|
57
|
+
def record_error_key
|
58
|
+
:posts
|
59
|
+
end
|
60
|
+
|
61
|
+
# [Required]
|
62
|
+
# Always validate if service can be executed based on your rules and ACL
|
63
|
+
# If this method is not implemented a NotImplementedError exception will be raised
|
64
|
+
def can_create_record?
|
65
|
+
unless valid_user?
|
66
|
+
return not_found_error!('users.not_found')
|
67
|
+
end
|
68
|
+
|
69
|
+
return forbidden_error!('errors.some_error') if (some_validation)
|
70
|
+
|
71
|
+
return bad_request_error!('errors.some_other_error') if (another_validation)
|
72
|
+
|
73
|
+
# remember to return true after all validations
|
74
|
+
# if you don't return true Service will not be able to create the record
|
75
|
+
return true
|
76
|
+
end
|
77
|
+
|
78
|
+
# [Optional]
|
79
|
+
# method called when save_error method call raises an exception
|
80
|
+
# this ocurr for example with ActiveRecord objects
|
81
|
+
# default: unprocessable_entity_error!(error)
|
82
|
+
def on_save_record_error(error)
|
83
|
+
logger.error(error)
|
84
|
+
|
85
|
+
if error.is_a?(ActiveRecord::RecordNotUnique)
|
86
|
+
return unprocessable_entity_error!(%s(posts.duplicate_record))
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# [Optional]
|
91
|
+
# custom scope for record, eg: @user.posts
|
92
|
+
# default is nil
|
93
|
+
def build_record_scope
|
94
|
+
nil
|
95
|
+
end
|
96
|
+
|
97
|
+
def valid_user?
|
98
|
+
valid_object?(@user, User)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
```
|
102
|
+
|
103
|
+
### BaseUpdateService Basic Markup
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
class SomeUpdateService < NiftyServices::BaseUpdateService
|
107
|
+
|
108
|
+
attr_reader :user
|
109
|
+
|
110
|
+
# [Required]
|
111
|
+
record_type RecordType, alias_name: :custom_alias_name
|
112
|
+
|
113
|
+
# You can freely override initialize method to receive more arguments
|
114
|
+
def initialize(record, user, options = {})
|
115
|
+
@user = user
|
116
|
+
super(record, options)
|
117
|
+
end
|
118
|
+
|
119
|
+
## Its a good convention to create a constant and not returning the plain
|
120
|
+
## array in `record_attributes_whitelist` since this can be accessed from others
|
121
|
+
## places, as: `SomeCreateService::WHITELIST_ATTRIBUTES`
|
122
|
+
WHITELIST_ATTRIBUTES = [
|
123
|
+
:safe_attribute_1,
|
124
|
+
:safe_attribute_2,
|
125
|
+
]
|
126
|
+
|
127
|
+
# [Required]
|
128
|
+
# You must setup whitelist attributes or define the method `record_attributes_whitelist` method
|
129
|
+
whitelist_attributes WHITELIST_ATTRIBUTES
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
# [Required]
|
134
|
+
# When a new instance of Service is created, the @options variables receive some
|
135
|
+
# values, eg: { user: { email: "...", name: "...."} }
|
136
|
+
# use record_attributes_hash to tell the Service from where to pull theses values
|
137
|
+
# eg: @options.fetch(:user, {})
|
138
|
+
# If this method is not implemented a NotImplementedError exception will be raised
|
139
|
+
def record_attributes_hash
|
140
|
+
@options.fetch(:data, {})
|
141
|
+
end
|
142
|
+
|
143
|
+
# [required]
|
144
|
+
# This is a VERY IMPORTANT point of attention
|
145
|
+
# always verify if @user has permissions to update the current @record object
|
146
|
+
# If this method is not implemented a NotImplementedError exception will be raised
|
147
|
+
def can_update_record?
|
148
|
+
@record.user_id == @user.id
|
149
|
+
end
|
150
|
+
|
151
|
+
# [Optional]
|
152
|
+
# This is the default implementation of update record, you may overwrite it
|
153
|
+
# to to custom updates (MOST OF TIME YOU DONT NEED TO DO THIS)
|
154
|
+
# only change this if you know what you are really doing
|
155
|
+
def update_record
|
156
|
+
@record.class.send(:update, @record.id, record_allowed_attributes)
|
157
|
+
end
|
158
|
+
|
159
|
+
# [optional]
|
160
|
+
# Any callback is optional, this is just a example
|
161
|
+
def after_success
|
162
|
+
if changed?
|
163
|
+
logger.info 'Successfully update record ID %s' % @record.id
|
164
|
+
logger.info 'Changed attributes are %s' % changed_attributes
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
```
|
169
|
+
|
170
|
+
---
|
171
|
+
|
172
|
+
### BaseDeleteService Basic Markup
|
173
|
+
|
174
|
+
```ruby
|
175
|
+
class SomeDeleteService < NiftyServices::BaseDeleteService
|
176
|
+
|
177
|
+
# You can freely override initialize method to receive more arguments
|
178
|
+
def initialize(record, user, options = {})
|
179
|
+
@user = user
|
180
|
+
super(record, options)
|
181
|
+
end
|
182
|
+
|
183
|
+
# [Required]
|
184
|
+
# record_type object must respond to :delete method
|
185
|
+
# But you can override `delete_method` method to do whatever you want
|
186
|
+
record_type RecordType, alias_name: :custom_alias_name
|
187
|
+
|
188
|
+
private
|
189
|
+
|
190
|
+
# [Required]
|
191
|
+
# This is a VERY IMPORTANT point of attention
|
192
|
+
# always verify if @user has permissions to delete the current @record object
|
193
|
+
# Hint: if @record respond_to `user_can_delete?(user)` you can remove this
|
194
|
+
# method and do the validation inside `user_can_delete(user)` method in @record
|
195
|
+
# If this method is not implemented a NotImplementedError exception will be raised
|
196
|
+
def can_delete_record?
|
197
|
+
@record.user_id == @user.id
|
198
|
+
end
|
199
|
+
|
200
|
+
# [optional]
|
201
|
+
# Any callback is optional, this is just a example
|
202
|
+
def after_success
|
203
|
+
logger.info('Successfully Deleted resource ID %s' % @record.id)
|
204
|
+
end
|
205
|
+
|
206
|
+
# [Optional]
|
207
|
+
# This is the default implementation of delete record, you may overwrite it
|
208
|
+
# to do custom delete (MOST OF TIME YOU DONT NEED TO DO THIS)
|
209
|
+
# only change this if you know what you are really doing
|
210
|
+
def delete_record
|
211
|
+
@record.delete
|
212
|
+
end
|
213
|
+
|
214
|
+
end
|
215
|
+
|
216
|
+
```
|
217
|
+
|
218
|
+
### BaseActionService Basic Markup
|
219
|
+
|
220
|
+
```ruby
|
221
|
+
class SomeCustomActionService < NiftyServices::BaseActionService
|
222
|
+
|
223
|
+
# [required]
|
224
|
+
# this is the action identifier used internally
|
225
|
+
# and to generate error messages
|
226
|
+
# see: invalid_action_error_key method
|
227
|
+
action_name :custom_action_name
|
228
|
+
|
229
|
+
private
|
230
|
+
# [Required]
|
231
|
+
# Always validate if Service can execute the action
|
232
|
+
# This method MUST return a boolean value indicating if Service can or not
|
233
|
+
# run the method `execute_service_action`
|
234
|
+
# If this method is not implemented a NotImplementedError exception will be raised
|
235
|
+
def can_execute_action?
|
236
|
+
# do some specific validation here, you can return errors such:
|
237
|
+
# return not_found_error!(%(users.invalid_user)) # returns false and avoid execution
|
238
|
+
return true
|
239
|
+
end
|
240
|
+
|
241
|
+
# [Required]
|
242
|
+
# The core function of BaseActionServices
|
243
|
+
# This method is called when all validations passes, so here you can put
|
244
|
+
# all logic for Service (eg: send mails, clear logs, any kind of action you want)
|
245
|
+
# If this method is not implemented a NotImplementedError exception will be raised
|
246
|
+
def execute_service_action
|
247
|
+
# (do some complex stuff)
|
248
|
+
end
|
249
|
+
|
250
|
+
# [Optional]
|
251
|
+
# You dont need to overwrite this method, just `record_error_key`
|
252
|
+
# But it's important you know how final message key will be created
|
253
|
+
# using the pattern below
|
254
|
+
def invalid_action_error_key
|
255
|
+
"#{record_error_key}.cant_execute_#{action_name}"
|
256
|
+
end
|
257
|
+
|
258
|
+
# [Required]
|
259
|
+
# Key used to created the error messages for this Service
|
260
|
+
# If this method is not implemented a NotImplementedError exception will be raised
|
261
|
+
def record_error_key
|
262
|
+
:users
|
263
|
+
end
|
264
|
+
end
|
265
|
+
```
|
266
|
+
|
267
|
+
---
|
268
|
+
|
269
|
+
### Next
|
270
|
+
|
271
|
+
See [CLI Generators](./cli.md)
|
data/docs/usage.md
ADDED
@@ -0,0 +1,204 @@
|
|
1
|
+
# NiftyServices documentation
|
2
|
+
|
3
|
+
---
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
NiftyServices provide a start basic service class for generic code which is `NiftyServices::BaseService`, the very basic service markup is demonstrated below:
|
8
|
+
|
9
|
+
### Basic Service Markup
|
10
|
+
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
class SemanticServiceName < NiftyServices::BaseService
|
14
|
+
|
15
|
+
def execute
|
16
|
+
execute_action do
|
17
|
+
success_response if do_something_complex
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def do_something_complex
|
22
|
+
# (...) some complex bussiness logic
|
23
|
+
return true
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def can_execute?
|
28
|
+
return forbidden_error!('errors.message_key') if some_condition
|
29
|
+
|
30
|
+
return not_found_error!('errors.message_key') if another_condition
|
31
|
+
|
32
|
+
return unprocessable_entity_error!('errors.message_key') if other_condition
|
33
|
+
|
34
|
+
# ok, this service can be executed
|
35
|
+
return true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
service = SemanticServiceName.new(options)
|
40
|
+
service.execute
|
41
|
+
```
|
42
|
+
|
43
|
+
---
|
44
|
+
|
45
|
+
### Ok, real world example plizzz
|
46
|
+
|
47
|
+
Lets work with a real and a little more complex example, an Service responsible to send daily news mail to users.
|
48
|
+
The code below shows basically everything you need to know about services structure, such: entry point, callbacks, authorization, error and success response handling, so after understanding this little piece of code, you will be **ready to code your own services**!
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
class DailyNewsMailSendService < NiftyServices::BaseService
|
52
|
+
|
53
|
+
before_execute do
|
54
|
+
log.info('Routine started at: %s' % Time.now)
|
55
|
+
end
|
56
|
+
|
57
|
+
after_execute do
|
58
|
+
log.info('Routine ended at: %s' % Time.now)
|
59
|
+
end
|
60
|
+
|
61
|
+
after_initialize do
|
62
|
+
user_data = [@user.name, @user.email]
|
63
|
+
log.info('Routine Details: Send daily news email to user %s(%s)' % user_data)
|
64
|
+
end
|
65
|
+
|
66
|
+
after_success do
|
67
|
+
log.info('Success sent daily news feed email to user')
|
68
|
+
end
|
69
|
+
|
70
|
+
before_error do
|
71
|
+
log.warn('Something went wrong')
|
72
|
+
end
|
73
|
+
|
74
|
+
after_error do
|
75
|
+
log.error('Error sending email to user. See details below :(')
|
76
|
+
log.error(errors)
|
77
|
+
end
|
78
|
+
|
79
|
+
attr_reader :user
|
80
|
+
|
81
|
+
def initialize(user, options = {})
|
82
|
+
@user = user
|
83
|
+
super(options)
|
84
|
+
end
|
85
|
+
|
86
|
+
def execute
|
87
|
+
execute_action do
|
88
|
+
success_response if send_mail_to_user
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
def can_execute?
|
94
|
+
unless valid_user?
|
95
|
+
# returns false
|
96
|
+
return not_found_error!('users.not_found')
|
97
|
+
end
|
98
|
+
|
99
|
+
unless @user.abble_to_receive_daily_news_mail?
|
100
|
+
# returns false
|
101
|
+
return forbidden_error!('users.already_received_daily_news_mail')
|
102
|
+
end
|
103
|
+
|
104
|
+
return true
|
105
|
+
end
|
106
|
+
|
107
|
+
def send_mail_to_user
|
108
|
+
# just to fake, a real implementation could be something like:
|
109
|
+
# @user.send_daily_news_mail!
|
110
|
+
return true
|
111
|
+
end
|
112
|
+
|
113
|
+
def valid_user?
|
114
|
+
# check if object is valid and is a User class type
|
115
|
+
valid_object?(@user, User)
|
116
|
+
end
|
117
|
+
|
118
|
+
# you can use `default_options` method to add default { keys => values } to @options
|
119
|
+
# so you can use the option_enabled?(key) to verify if option is enabled
|
120
|
+
# or option_disabled?(key) to verify is option is disabled
|
121
|
+
# This default values can be override when creating new instance of Service, eg:
|
122
|
+
# DailyNewsMailSendService.new(User.last, validate_api_key: false)
|
123
|
+
def default_options
|
124
|
+
{ validate_api_key: true }
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class User < Struct.new(:name, :email)
|
129
|
+
# just to play around with results
|
130
|
+
def abble_to_receive_daily_news_mail?
|
131
|
+
rand(10) < 5
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
user = User.new('Rafael Fidelis', 'rafa_fidelis@yahoo.com.br')
|
136
|
+
|
137
|
+
# Default logger is NiftyService.config.logger = Logger.new('/dev/null')
|
138
|
+
service = DailyNewsMailSendService.new(user, logger: Logger.new('daily_news.log'))
|
139
|
+
service.execute
|
140
|
+
```
|
141
|
+
|
142
|
+
### Sample outputs results
|
143
|
+
|
144
|
+
#### :smile: Success:
|
145
|
+
|
146
|
+
```
|
147
|
+
I, [2016-07-15T17:13:40.092854 #2480] INFO -- : Routine Details: Send daily news email to user
|
148
|
+
Rafael Fidelis(rafa_fidelis@yahoo.com.br)
|
149
|
+
|
150
|
+
I, [2016-07-15T17:13:40.092987 #2480] INFO -- : Routine started at: 2016-07-15 17:13:40 -0300
|
151
|
+
|
152
|
+
I, [2016-07-15T17:13:40.093143 #2480] INFO -- : Success sent daily news feed email to user
|
153
|
+
|
154
|
+
I, [2016-07-15T17:13:40.093242 #2480] INFO -- : Routine ended at: 2016-07-15 17:13:40 -0300
|
155
|
+
|
156
|
+
|
157
|
+
```
|
158
|
+
|
159
|
+
#### :weary: Error:
|
160
|
+
|
161
|
+
```
|
162
|
+
I, [2016-07-15T17:12:10.954792 #756] INFO -- : Routine Details: Send daily news email to user
|
163
|
+
Rafael Fidelis(rafa_fidelis@yahoo.com.br)
|
164
|
+
|
165
|
+
I, [2016-07-15T17:12:10.955025 #756] INFO -- : Routine started at: 2016-07-15 17:12:10 -0300
|
166
|
+
|
167
|
+
W, [2016-07-15T17:12:10.955186 #756] WARN -- : Something went wrong
|
168
|
+
|
169
|
+
E, [2016-07-15T17:12:11.019645 #756] ERROR -- : Error sending email to user. See details below :(
|
170
|
+
|
171
|
+
E, [2016-07-15T17:12:11.019838 #756] ERROR -- : ["User has already received daily news mail today"]
|
172
|
+
|
173
|
+
I, [2016-07-15T17:12:11.020073 #756] INFO -- : Routine ended at: 2016-07-15 17:12:11 -0300
|
174
|
+
|
175
|
+
```
|
176
|
+
|
177
|
+
<br />
|
178
|
+
|
179
|
+
### Wrapping things up
|
180
|
+
|
181
|
+
The code above demonstrate a very basic example of **how dead easy** is to work with Services, let me clarify some things to your better understanding:
|
182
|
+
|
183
|
+
* ☑ All services classes must inherit from `NiftyServices::BaseService`
|
184
|
+
|
185
|
+
* ☑ For convention(but not a rule) all services must expose only `execute`(and of course, `initialize`) as public methods.
|
186
|
+
|
187
|
+
* ☑ `execute_action(&block)` **MUST** be called to properly setup things in execution context.
|
188
|
+
|
189
|
+
* ☑ `can_execute?` must be **ALWAYS** implemented in service classes, **ALWAYS**, this ensure that your code will **safely runned**.
|
190
|
+
Note: A `NotImplementedError` exception will be raised if service won't define your own `can_execute?` method.
|
191
|
+
|
192
|
+
* ☑ There's a very simple helper functions for marking result as success/fail (eg: `unprocessable_entity_error!` or `success_response`).
|
193
|
+
|
194
|
+
* ☑ Simple DSL for actions callbacks inside current execution context. (eg: `after_success` or `before_error`)
|
195
|
+
Note: You don't need to use the DSL if you don't want, you can simply define the methods(such as: `private def after_success; do_something; end`
|
196
|
+
|
197
|
+
This is the very basic concept of creating and executing a service object, now we need to know how to work with responses to get the most of our services, for this, let's digg in the mainly public API methods of `NiftyService::BaseService` class:
|
198
|
+
|
199
|
+
|
200
|
+
---
|
201
|
+
|
202
|
+
### Next
|
203
|
+
|
204
|
+
See [Crud Objects API Interface](./api.md)
|