nifty_services 0.0.5 → 0.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/.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)
|