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.
@@ -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)
@@ -1,7 +1,8 @@
1
+ require 'logger'
2
+
1
3
  require 'nifty_services/version'
2
- require 'active_support/core_ext/object/blank'
3
- require 'active_support/core_ext/hash/keys'
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(&block)
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 user_can_execute_action?
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 user_can_execute_action?
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(user, options = {})
5
- @user = user
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 save_record
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
- @record.save
36
+ save_record
30
37
  rescue => e
31
38
  on_save_record_error(e)
32
- return false
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.present?
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
- if record_type.present?
54
- return build_from_record_type(record_allowed_attributes)
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
- return not_implemented_exception(__method__)
66
+ @temp_record = record_type.public_send(:new, record_allowed_attributes)
58
67
  end
59
68
 
60
- def build_from_record_type(params)
61
- if !build_record_scope.nil? && build_record_scope.respond_to?(:build)
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 can_execute?
69
- if validate_ip_on_create? && !can_create_with_ip?
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 validate_user? && !valid_user?
74
- return not_found_error!(invalid_user_error_key)
76
+ if !build_record_scope.nil? && build_record_scope.respond_to?(:new)
77
+ scope = build_record_scope
75
78
  end
76
79
 
77
- return true
80
+ scope.new(attributes)
78
81
  end
79
82
 
80
- def can_create_record?
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 user_can_create_record?
87
+ def can_create_record?
89
88
  not_implemented_exception(__method__)
90
89
  end
91
90
 
92
91
  def can_execute_action?
93
- return can_create_record?
94
- end
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
- def build_record_scope
106
- nil
96
+ return true
107
97
  end
108
98
 
109
- def user_cant_create_error_key
110
- "#{record_error_key}.user_cant_create"
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 include_concern(namespace, concern_type)
21
- _module = "#{services_concern_namespace}::#{namespace.to_s.camelize}::#{concern_type.to_s.camelize}"
17
+ def whitelist_attributes(*attrs)
18
+ raise "Invalid whitelist attributes(#{attrs}) array" unless attrs.is_a?(Array)
22
19
 
23
- self.include(_module.constantize)
24
- end
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
- def services_concern_namespace
27
- NiftyServices.config.service_concerns_namespace
30
+ attributes
28
31
  end
29
32
 
30
- alias_method :concern, :include_concern
33
+ def get_whitelist_attributes
34
+ []
35
+ end
31
36
  end
32
37
 
33
- def initialize(record, user, options = {})
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
- record_type.to_s.pluralize.underscore
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
- if destroy_record
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
- bad_request_error(@record.errors)
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 destroy_record
20
- @record.try(:destroy) || @record.try(:delete)
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
- unless user_can_delete_record?
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
- return can_delete_record?
45
- end
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
- @record.user_can_delete?(@user)
51
+ return true
51
52
  end
52
53
 
53
- def user_cant_delete_error_key
54
- "#{record_error_key}.user_cant_delete"
54
+ def cant_delete_error_key
55
+ "#{record_error_key}.cant_delete"
55
56
  end
56
57
  end
57
58
  end