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
@@ -0,0 +1,145 @@
|
|
1
|
+
# NiftyServices documentation
|
2
|
+
|
3
|
+
---
|
4
|
+
|
5
|
+
## Web Frameworks Integrations
|
6
|
+
|
7
|
+
### Rails <a name="frameworks-rails"></a>
|
8
|
+
|
9
|
+
You need a very minimal setup to integrate with your existing or new Rails application. I prefer to put my services files inside the `lib/services` folder, cause this allow better namespacing configuration over `app/services`, but this is up to you to decide.
|
10
|
+
|
11
|
+
First thing to do is add `lib/` folder in `autoload` path, place the following in your `config/application.rb`
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
# config/application.rb
|
15
|
+
config.paths.add(File.join(Rails.root, 'lib'), glob: File.join('**', '*.rb'))
|
16
|
+
|
17
|
+
config.autoload_paths << Rails.root.join('lib')
|
18
|
+
```
|
19
|
+
|
20
|
+
Second, create `lib/services` directory:
|
21
|
+
|
22
|
+
`$ mkdir -p lib/services/v1/users`
|
23
|
+
|
24
|
+
Next, configure:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
NiftyServices.configure do |config|
|
28
|
+
config.i18n_namespace = 'my_app'
|
29
|
+
end
|
30
|
+
```
|
31
|
+
**Note**: See [Configurations](./configuration.md) section to see all available configs
|
32
|
+
|
33
|
+
Create your first service:
|
34
|
+
|
35
|
+
```
|
36
|
+
$ touch lib/services/v1/users/create_service.rb
|
37
|
+
```
|
38
|
+
|
39
|
+
Use in your controller:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
class UsersController < BaseController
|
43
|
+
def create
|
44
|
+
service = Services::V1::Users::CreateService.new(params).execute
|
45
|
+
|
46
|
+
default_response = { status: service.response_status, status_code: service.response_status_code }
|
47
|
+
|
48
|
+
if service.success?
|
49
|
+
response = { user: service.user, subscription: service.subscription }
|
50
|
+
else
|
51
|
+
response = { error: true, errors: service.errors }
|
52
|
+
end
|
53
|
+
|
54
|
+
render json: default_response.merge(response), status: service.response_status
|
55
|
+
end
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
This can be even better if you move response code to a helper:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
# helpers/users_helper.rb
|
63
|
+
module UsersHelper
|
64
|
+
include GenericHelpers
|
65
|
+
|
66
|
+
def response_for_user_create_service(service)
|
67
|
+
success_response = { user: service.user, subscription: service.subscription }
|
68
|
+
generic_response_for_service(service, success_response)
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
```
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
# helpers/generic_helper.rb
|
76
|
+
module GenericHelper
|
77
|
+
|
78
|
+
# THIS IS GREAT, you can use this method to standardize ALL of your
|
79
|
+
# endpoints responses, THIS IS SO FUCKING COOL!
|
80
|
+
def generic_response_for_service(service, success_response)
|
81
|
+
default_response = {
|
82
|
+
status: service.response_status,
|
83
|
+
status_code: service.response_status_code,
|
84
|
+
success: service.success?
|
85
|
+
}
|
86
|
+
|
87
|
+
if service.success?
|
88
|
+
response = success_response
|
89
|
+
else
|
90
|
+
response = {
|
91
|
+
error: true,
|
92
|
+
errors: service.errors
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
default_response.merge(response)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
Changing controller again: (looks so readable now <3)
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
# controllers/users_controller.rb
|
104
|
+
class UsersController < BaseController
|
105
|
+
def create
|
106
|
+
service = Services::V1::Users::CreateService.new(params).execute
|
107
|
+
|
108
|
+
render json: response_for_user_create_service(service), status: service.response_status
|
109
|
+
end
|
110
|
+
end
|
111
|
+
```
|
112
|
+
|
113
|
+
Well done sir! Did you read the comments in `generic_response_for_service`? Read it and think a little about this and prepare yourself for having orgasms when you realize how fuck awesome this will be for your API's. Need mode? Checkout [Sample Standartized API with NiftyServices Repository](http://github.com/fidelisrafael/nifty_services-api_sample)
|
114
|
+
|
115
|
+
---
|
116
|
+
|
117
|
+
### Grape/Sinatra/Padrino/Hanami/Rack <a name="frameworks-rack"></a>
|
118
|
+
|
119
|
+
Well, the integration here don't variate too much from Rails, just follow the steps:
|
120
|
+
|
121
|
+
**1 -** Decide where you'll put your services
|
122
|
+
|
123
|
+
**2 -** Code that dam amazing services!
|
124
|
+
|
125
|
+
**3 -** Instantiate the service in your framework entry point
|
126
|
+
|
127
|
+
**4 -** Create helpers to handle service response
|
128
|
+
|
129
|
+
**5 -** Be happy and go party!
|
130
|
+
|
131
|
+
---
|
132
|
+
|
133
|
+
## Integration Examples
|
134
|
+
|
135
|
+
Need examples of integrations fully working? Check out one of the following repositories:
|
136
|
+
|
137
|
+
[NiftyServices - Sinatra Sample](https://github.com/fidelisrafael/nifty_services-sinatra_example)
|
138
|
+
NiftyServices - Grape Sample
|
139
|
+
NiftyServices - Rails Sample
|
140
|
+
|
141
|
+
---
|
142
|
+
|
143
|
+
### Next
|
144
|
+
|
145
|
+
See [Basic Services class Markups](./services_markup.md)
|
data/lib/nifty_services.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
1
3
|
require 'nifty_services/version'
|
2
|
-
require '
|
3
|
-
require '
|
4
|
-
require 'active_support/core_ext/string/inflections'
|
4
|
+
require 'nifty_services/support/hash'
|
5
|
+
require 'nifty_services/support/string'
|
5
6
|
|
6
7
|
module NiftyServices
|
7
8
|
|
@@ -16,12 +17,8 @@ module NiftyServices
|
|
16
17
|
autoload :Errors, 'nifty_services/errors'
|
17
18
|
autoload :Util, 'nifty_services/util'
|
18
19
|
|
19
|
-
module Extensions
|
20
|
-
autoload :CallbacksInterface, 'nifty_services/extensions/callbacks_interface'
|
21
|
-
end
|
22
|
-
|
23
20
|
class << self
|
24
|
-
def configuration
|
21
|
+
def configuration
|
25
22
|
@configuration ||= Configuration.new
|
26
23
|
|
27
24
|
yield(@configuration) if block_given?
|
@@ -11,25 +11,22 @@ module NiftyServices
|
|
11
11
|
execute_action do
|
12
12
|
with_before_and_after_callbacks(:action) do
|
13
13
|
# here user can
|
14
|
-
execute_service_action
|
15
|
-
|
16
|
-
if valid?
|
17
|
-
success_response
|
18
|
-
else
|
19
|
-
errors = action_errors
|
20
|
-
bad_request_error(errors) if errors.present?
|
14
|
+
with_before_and_after_callbacks(:execute_service_action) do
|
15
|
+
execute_service_action
|
21
16
|
end
|
17
|
+
|
18
|
+
success_response if valid?
|
22
19
|
end
|
23
20
|
end
|
24
21
|
end
|
25
22
|
|
26
23
|
private
|
27
24
|
def action_errors
|
28
|
-
|
25
|
+
@errors
|
29
26
|
end
|
30
27
|
|
31
28
|
def can_execute?
|
32
|
-
unless
|
29
|
+
unless can_execute_action?
|
33
30
|
return (valid? ? unprocessable_entity_error!(invalid_action_error_key) : false)
|
34
31
|
end
|
35
32
|
|
@@ -40,7 +37,7 @@ module NiftyServices
|
|
40
37
|
"#{record_error_key}.cant_execute_#{action_name}"
|
41
38
|
end
|
42
39
|
|
43
|
-
def
|
40
|
+
def can_execute_action?
|
44
41
|
not_implemented_exception(__method__)
|
45
42
|
end
|
46
43
|
|
@@ -1,18 +1,17 @@
|
|
1
1
|
module NiftyServices
|
2
2
|
class BaseCreateService < BaseCrudService
|
3
3
|
|
4
|
-
def initialize(
|
5
|
-
|
6
|
-
super(nil, user, options)
|
4
|
+
def initialize(options = {})
|
5
|
+
super(nil, options)
|
7
6
|
end
|
8
7
|
|
9
8
|
def execute
|
10
9
|
execute_action do
|
11
10
|
with_before_and_after_callbacks(:create) do
|
12
11
|
if can_execute_action?
|
13
|
-
@record = build_record
|
12
|
+
@record = with_before_and_after_callbacks(:build_record) { build_record }
|
14
13
|
|
15
|
-
if
|
14
|
+
if try_to_save_record
|
16
15
|
after_execute_success_response
|
17
16
|
else
|
18
17
|
errors = create_error_response(@record)
|
@@ -25,11 +24,20 @@ module NiftyServices
|
|
25
24
|
|
26
25
|
private
|
27
26
|
def save_record
|
27
|
+
save_method = NiftyServices.configuration.save_record_method
|
28
|
+
|
29
|
+
return save_method.call(@record) if save_method.respond_to?(:call)
|
30
|
+
|
31
|
+
@record.public_send(save_method)
|
32
|
+
end
|
33
|
+
|
34
|
+
def try_to_save_record
|
28
35
|
begin
|
29
|
-
|
36
|
+
save_record
|
30
37
|
rescue => e
|
31
38
|
on_save_record_error(e)
|
32
|
-
|
39
|
+
ensure
|
40
|
+
return @record.valid?
|
33
41
|
end
|
34
42
|
end
|
35
43
|
|
@@ -42,7 +50,7 @@ module NiftyServices
|
|
42
50
|
end
|
43
51
|
|
44
52
|
def after_error_response(errors)
|
45
|
-
unprocessable_entity_error(errors) if errors.
|
53
|
+
unprocessable_entity_error(errors) if @errors.empty?
|
46
54
|
end
|
47
55
|
|
48
56
|
def after_execute_success_response
|
@@ -50,64 +58,46 @@ module NiftyServices
|
|
50
58
|
end
|
51
59
|
|
52
60
|
def build_record
|
53
|
-
|
54
|
-
|
61
|
+
unless record_type.nil?
|
62
|
+
# initialize @temp_record to be used in after_build_record callback
|
63
|
+
return @temp_record = build_from_record_type(record_allowed_attributes)
|
55
64
|
end
|
56
65
|
|
57
|
-
|
66
|
+
@temp_record = record_type.public_send(:new, record_allowed_attributes)
|
58
67
|
end
|
59
68
|
|
60
|
-
def
|
61
|
-
|
62
|
-
return build_record_scope.send(:build, params)
|
63
|
-
end
|
64
|
-
|
65
|
-
record_type.send(:new, params)
|
69
|
+
def build_record_scope
|
70
|
+
nil
|
66
71
|
end
|
67
72
|
|
68
|
-
def
|
69
|
-
|
70
|
-
return forbidden_error!(%s(users.ip_temporarily_blocked))
|
71
|
-
end
|
73
|
+
def build_from_record_type(attributes)
|
74
|
+
scope = record_type
|
72
75
|
|
73
|
-
if
|
74
|
-
|
76
|
+
if !build_record_scope.nil? && build_record_scope.respond_to?(:new)
|
77
|
+
scope = build_record_scope
|
75
78
|
end
|
76
79
|
|
77
|
-
|
80
|
+
scope.new(attributes)
|
78
81
|
end
|
79
82
|
|
80
|
-
def
|
81
|
-
unless user_can_create_record?
|
82
|
-
return (valid? ? forbidden_error!(user_cant_create_error_key) : false)
|
83
|
-
end
|
84
|
-
|
83
|
+
def can_execute?
|
85
84
|
return true
|
86
85
|
end
|
87
86
|
|
88
|
-
def
|
87
|
+
def can_create_record?
|
89
88
|
not_implemented_exception(__method__)
|
90
89
|
end
|
91
90
|
|
92
91
|
def can_execute_action?
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
def can_create_with_ip?
|
97
|
-
true
|
98
|
-
end
|
99
|
-
|
100
|
-
def validate_ip_on_create?
|
101
|
-
# TODO: Use NiftyService.config.validate_ip_on_create?
|
102
|
-
false
|
103
|
-
end
|
92
|
+
unless can_create_record?
|
93
|
+
return (valid? ? forbidden_error!(cant_create_error_key) : false)
|
94
|
+
end
|
104
95
|
|
105
|
-
|
106
|
-
nil
|
96
|
+
return true
|
107
97
|
end
|
108
98
|
|
109
|
-
def
|
110
|
-
"#{record_error_key}.
|
99
|
+
def cant_create_error_key
|
100
|
+
"#{record_error_key}.cant_create"
|
111
101
|
end
|
112
102
|
end
|
113
103
|
end
|
@@ -1,7 +1,4 @@
|
|
1
1
|
module NiftyServices
|
2
|
-
module Concerns
|
3
|
-
end
|
4
|
-
|
5
2
|
class BaseCrudService < BaseService
|
6
3
|
|
7
4
|
attr_reader :record
|
@@ -17,22 +14,29 @@ module NiftyServices
|
|
17
14
|
alias_method record_alias.to_sym, :record
|
18
15
|
end
|
19
16
|
|
20
|
-
def
|
21
|
-
|
17
|
+
def whitelist_attributes(*attrs)
|
18
|
+
raise "Invalid whitelist attributes(#{attrs}) array" unless attrs.is_a?(Array)
|
22
19
|
|
23
|
-
|
24
|
-
|
20
|
+
attributes = *attrs.flatten.map(&:to_sym)
|
21
|
+
|
22
|
+
define_method :record_attributes_whitelist do
|
23
|
+
attributes
|
24
|
+
end
|
25
|
+
|
26
|
+
define_singleton_method :get_whitelist_attributes do
|
27
|
+
attributes
|
28
|
+
end
|
25
29
|
|
26
|
-
|
27
|
-
NiftyServices.config.service_concerns_namespace
|
30
|
+
attributes
|
28
31
|
end
|
29
32
|
|
30
|
-
|
33
|
+
def get_whitelist_attributes
|
34
|
+
[]
|
35
|
+
end
|
31
36
|
end
|
32
37
|
|
33
|
-
def initialize(record,
|
38
|
+
def initialize(record, options = {})
|
34
39
|
@record = record
|
35
|
-
@user = user
|
36
40
|
|
37
41
|
super(options)
|
38
42
|
end
|
@@ -64,38 +68,9 @@ module NiftyServices
|
|
64
68
|
alias :record_safe_attributes :record_allowed_attributes
|
65
69
|
|
66
70
|
private
|
67
|
-
def array_values_from_hash(options, key, root = nil)
|
68
|
-
options = options.symbolize_keys
|
69
|
-
|
70
|
-
if root.present?
|
71
|
-
options = (options[root.to_sym] || {}).symbolize_keys
|
72
|
-
end
|
73
|
-
|
74
|
-
return [] unless options.key?(key.to_sym)
|
75
|
-
|
76
|
-
values = options[key.to_sym]
|
77
|
-
|
78
|
-
return values if values.is_a?(Array)
|
79
|
-
|
80
|
-
array_values_from_string(values)
|
81
|
-
end
|
82
|
-
|
83
|
-
def invalid_user_error_key
|
84
|
-
%s(users.not_found)
|
85
|
-
end
|
86
|
-
|
87
|
-
def validate_user?
|
88
|
-
true
|
89
|
-
end
|
90
|
-
|
91
|
-
alias :array_values_from_params :array_values_from_hash
|
92
|
-
|
93
|
-
def array_values_from_string(string)
|
94
|
-
string.to_s.split(/\,/).map(&:squish)
|
95
|
-
end
|
96
|
-
|
97
71
|
def record_error_key
|
98
|
-
|
72
|
+
# very simple and poor way to pluralize string
|
73
|
+
record_type.to_s.underscore + "s"
|
99
74
|
end
|
100
75
|
|
101
76
|
def valid_record?
|
@@ -5,10 +5,12 @@ module NiftyServices
|
|
5
5
|
execute_action do
|
6
6
|
with_before_and_after_callbacks(:delete) do
|
7
7
|
if can_execute_action?
|
8
|
-
|
8
|
+
deleted_record = with_before_and_after_callbacks(:delete_record) { delete_record }
|
9
|
+
|
10
|
+
if deleted_record
|
9
11
|
success_response
|
10
12
|
else
|
11
|
-
|
13
|
+
unprocessable_entity_error!(@record.errors)
|
12
14
|
end
|
13
15
|
end
|
14
16
|
end
|
@@ -16,8 +18,17 @@ module NiftyServices
|
|
16
18
|
end
|
17
19
|
|
18
20
|
private
|
19
|
-
def
|
20
|
-
|
21
|
+
def delete_record
|
22
|
+
delete_method = NiftyServices.configuration.delete_record_method
|
23
|
+
|
24
|
+
if delete_method.respond_to?(:call)
|
25
|
+
delete_method.call(@record)
|
26
|
+
else
|
27
|
+
@record.public_send(delete_method)
|
28
|
+
end
|
29
|
+
|
30
|
+
# initialize @temp_record to be used in after_delete_record callback
|
31
|
+
@temp_record = @record
|
21
32
|
end
|
22
33
|
|
23
34
|
def can_execute?
|
@@ -25,33 +36,23 @@ module NiftyServices
|
|
25
36
|
return not_found_error!("#{record_error_key}.not_found")
|
26
37
|
end
|
27
38
|
|
28
|
-
if validate_user? && !valid_user?
|
29
|
-
return not_found_error!(invalid_user_error_key)
|
30
|
-
end
|
31
|
-
|
32
39
|
return true
|
33
40
|
end
|
34
41
|
|
35
42
|
def can_delete_record?
|
36
|
-
|
37
|
-
return (valid? ? forbidden_error!(user_can_delete_error_key) : false)
|
38
|
-
end
|
39
|
-
|
40
|
-
return true
|
43
|
+
not_implemented_exception(__method__)
|
41
44
|
end
|
42
45
|
|
43
46
|
def can_execute_action?
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
def user_can_delete_record?
|
48
|
-
return not_implemented_exception(__method__) unless @record.respond_to?(:user_can_delete?)
|
47
|
+
unless can_delete_record?
|
48
|
+
return (valid? ? forbidden_error!(cant_delete_error_key) : false)
|
49
|
+
end
|
49
50
|
|
50
|
-
|
51
|
+
return true
|
51
52
|
end
|
52
53
|
|
53
|
-
def
|
54
|
-
"#{record_error_key}.
|
54
|
+
def cant_delete_error_key
|
55
|
+
"#{record_error_key}.cant_delete"
|
55
56
|
end
|
56
57
|
end
|
57
58
|
end
|