nifty_services 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2388b61c2fa5d1fed162e0f46b7014fb780fda46
4
- data.tar.gz: 2889984fdc020de82e987c288680f48814b22ed5
3
+ metadata.gz: 4b51ddb57c9b14189952188821a22c36ea9b7c35
4
+ data.tar.gz: 1bf6174e4899e88817ea3ec77db991d2cf249cdc
5
5
  SHA512:
6
- metadata.gz: 49d2e94d0aae898be5056584865368b69131061d7a910edc1c6d52aa3530618899d8c9153ba264cd4c2cb245f65f46b09106a5d10a4b288eb85b544851f29fc8
7
- data.tar.gz: 3c93bf62d33f637435c3b47c93160e3b873ae0169e1255c3a6a121ba632e68b32cbe103006d1bd74170fcbfc5042fa279a87aac5e86b111ec9d6581a8843170a
6
+ metadata.gz: b098ca14f8ad2789d6dc7e0fc4026452a79d30697f241d31d6b74dfb7397e50b89df681e7648648550e66b66e2f2ec52605dc96359a741ead9c4ebf456645024
7
+ data.tar.gz: ee3633820cc0690f469acd67a74c3ff63d6f86d19f08f92b4b09d26296cc448cfb003159295485ab5c4a47532defc8ca7072c302e523ee2cdbfcd7a69db251c8
data/README.md CHANGED
@@ -4,18 +4,19 @@
4
4
 
5
5
  Nifty Services comes to solve your Ruby applications(*including but not limited to* Rails, Grape, Sinatra, and plain Ruby) code mess with **simplicity in mind**!
6
6
 
7
- NiftyServices provider a very nifty, simple & clear API to **organize and reuse** your application **domain logic in plain Ruby Services Objects** turning your codebase in a very extensible, standardized and reusable components.
7
+ NiftyServices provides a very nifty, simple & clear API to **organize and reuse** your application **domain logic in plain Ruby Services Objects** turning your codebase in a very extensible, standardized and reusable components.
8
8
 
9
9
  **Most important:** You and your team win what I consider the best benefit when using Nifty Services: **Easily and scalable maintained code.**
10
10
  Believe me, you'll fall in :heart_eyes: with this small piece of code, keep reading!
11
11
 
12
- This gem was designed and conventioned to be used specially with **Web API applications**, but this is just a convention, you can use it's even with [shoes (for desktop apps)](https://github.com/shoes/shoes) applications if you want, for example.
12
+ This gem was designed and conventioned to be used specially with **Web API applications**, but this is just a convention, you can use it even with [shoes (for desktop apps)](https://github.com/shoes/shoes) applications if you want, for example.
13
13
 
14
14
  #### :book: I know, this README is very huge
15
15
 
16
16
  As you can see, this README needs some time to be full read, but is very difficulty to explain all things, concepts and philosophy of this gem without writing a lot, we can't escape this :(
17
17
 
18
- But remember one thing: This is a **tecnical documentation**, not a blog post, I'm pretty sure you can take 1 or 2 hours + :coffee: to better understand all NiftyServices can do for you and your project. Good reading, and if you have some question, [please let me know](/issues/new).
18
+ But remember one thing: This is a **tecnical documentation**, not a blog post, I'm pretty sure you can take about 30 minutes + some cups of :coffee: to better understand all NiftyServices can
19
+ do for you and your project. Good reading, and if you have some question, [please let me know](issues/new).
19
20
 
20
21
  ---
21
22
 
@@ -60,6 +61,11 @@ But remember one thing: This is a **tecnical documentation**, not a blog post, I
60
61
  * [Web Frameworks integration](#web-frameworks-integrations)
61
62
  * [Ruby on Rails](#frameworks-rails)
62
63
  * [Grape/Sinatra/Rack](#frameworks-rack)
64
+ * [ Basic Services class Markups](#pray-basic-service-markups-raised_hands)
65
+ * [BaseCreateService Basic Markup](#basecreateservice-basic-markup)
66
+ * [BaseUpdateService Basic Markup](#baseupdateservice-basic-markup)
67
+ * [BaseDeleteService Basic Markup](#basedeleteservice-basic-markup)
68
+ * [BaseActionService Basic Markup](#baseactionservice-basic-markup)
63
69
  * [CLI Generators](#cli-generators)
64
70
  * [Roadmap](#roadmap)
65
71
  * [Development](#computer-development)
@@ -99,7 +105,7 @@ Now you know the basic concepts and philosophy of `NiftyServices`, lets start wo
99
105
  Add this line to your application's Gemfile:
100
106
 
101
107
  ```ruby
102
- gem 'nifty_services'
108
+ gem 'nifty_services', '~> 0.0.5'
103
109
  ```
104
110
 
105
111
  And then execute:
@@ -139,7 +145,7 @@ class SemanticServiceName < NiftyServices::BaseService
139
145
 
140
146
  return not_found_error!('errors.message_key') if another_condition
141
147
 
142
- return unprocessable_entity_error('errors.message_key') if other_condition
148
+ return unprocessable_entity_error!('errors.message_key') if other_condition
143
149
 
144
150
  # ok, this service can be executed
145
151
  return true
@@ -208,21 +214,31 @@ class DailyNewsMailSendService < NiftyServices::BaseService
208
214
 
209
215
  unless @user.abble_to_receive_daily_news_mail?
210
216
  # returns false
211
- return forbidden_error!('users.yet_received_daily_news_mail')
217
+ return forbidden_error!('users.already_received_daily_news_mail')
212
218
  end
213
219
 
214
220
  return true
215
221
  end
222
+
216
223
  def send_mail_to_user
217
224
  # just to fake, a real implementation could be something like:
218
225
  # @user.send_daily_news_mail!
219
226
  return true
220
227
  end
221
-
228
+
222
229
  def valid_user?
223
230
  # check if object is valid and is a User class type
224
231
  valid_object?(@user, User)
225
232
  end
233
+
234
+ # you can use `default_options` method to add default { keys => values } to @options
235
+ # so you can use the option_enabled?(key) to verify if option is enabled
236
+ # or option_disabled?(key) to verify is option is disabled
237
+ # This default values can be override when creating new instance of Service, eg:
238
+ # DailyNewsMailSendService.new(User.last, validate_api_key: false)
239
+ def default_options
240
+ { validate_api_key: true }
241
+ end
226
242
  end
227
243
 
228
244
  class User < Struct.new(:name, :email)
@@ -268,7 +284,7 @@ W, [2016-07-15T17:12:10.955186 #756] WARN -- : Something went wrong
268
284
 
269
285
  E, [2016-07-15T17:12:11.019645 #756] ERROR -- : Error sending email to user. See details below :(
270
286
 
271
- E, [2016-07-15T17:12:11.019838 #756] ERROR -- : ["User yet received daily news mail today"]
287
+ E, [2016-07-15T17:12:11.019838 #756] ERROR -- : ["User has already received daily news mail today"]
272
288
 
273
289
  I, [2016-07-15T17:12:11.020073 #756] INFO -- : Routine ended at: 2016-07-15 17:12:11 -0300
274
290
 
@@ -387,7 +403,7 @@ error!(409, :conflict_error, reason: 'unkown')
387
403
 
388
404
  #### Custom error response methods
389
405
 
390
- But you can always add new convenience errors methods via API, this way you gain more expressivity and sintax sugar:
406
+ But you can always add new convenience errors methods via API, this way you will have more expressivity and sintax sugar:
391
407
 
392
408
  ```ruby
393
409
  ## API
@@ -397,7 +413,7 @@ NiftyServices.add_response_error_method(status, status_code)
397
413
 
398
414
  NiftyServices.add_response_error_method(:conflict, 409)
399
415
 
400
- ## now you gain the methods:
416
+ ## now you have the methods:
401
417
 
402
418
  ## conflict_error(:conflict_error)
403
419
  ## conflit_error!(:conflict_error)
@@ -409,7 +425,7 @@ NiftyServices.add_response_error_method(:conflict, 409)
409
425
 
410
426
  So, until now we saw how to use `NiftyServices::BaseService` to create generic services to couple specific domain logic for actions, this is very usefull, but things get a lot better when you're working with **CRUD** actions for your api.
411
427
 
412
- Above, an example of **Create, Update and Delete** CRUD services for `Post` resource:
428
+ Follow an example of **Create, Update and Delete** CRUD services for `Post` resource:
413
429
 
414
430
  ## :white_check_mark: CRUD: Create
415
431
 
@@ -428,13 +444,14 @@ class PostCreateService < NiftyServices::BaseCreateService
428
444
 
429
445
  WHITELIST_ATTRIBUTES = [:title, :content]
430
446
 
431
- def record_params_whitelist
447
+ def record_attributes_whitelist
432
448
  WHITELIST_ATTRIBUTES
433
449
  end
434
450
 
435
- def build_record
436
- # record_allowed_params auto magically use record_params_whitelist to remove unsafe attributes
437
- @user.posts.build(record_allowed_params)
451
+ # use custom scope to create the record
452
+ # scope returned below must respond_to :build instance method
453
+ def build_record_scope
454
+ @user.posts
438
455
  end
439
456
 
440
457
  # this key is used for I18n translations
@@ -445,9 +462,9 @@ class PostCreateService < NiftyServices::BaseCreateService
445
462
  def user_can_create_record?
446
463
  # (here you can do any kind of validation, eg:)
447
464
  # check if user is trying to recreate a recent resource
448
- # this will return false if user yet created a post with
465
+ # this will return false if user has already created a post with
449
466
  # this title in the last 30 seconds (usefull to ban bots)
450
- @user.posts.exists(title: record_allowed_params[:title], created_at: "NOW() - interval(30 seconds)")
467
+ @user.posts.exists(title: record_allowed_attributes[:title], created_at: "NOW() - interval(30 seconds)")
451
468
  end
452
469
  end
453
470
 
@@ -534,7 +551,7 @@ class PostUpdateService < NiftyServices::BaseUpdateService
534
551
 
535
552
  WHITELIST_ATTRIBUTES = [:title, :content]
536
553
 
537
- def record_allowed_params
554
+ def record_allowed_attributes
538
555
  WHITELIST_ATTRIBUTES
539
556
  end
540
557
 
@@ -817,9 +834,9 @@ NiftyServices::BaseCreateService.class_eval do
817
834
  end
818
835
  end
819
836
 
820
- # This register an callback for ALL services who inherit from `NiftyServices::BaseCreateService`
837
+ # This register a callback for ALL services who inherit from `NiftyServices::BaseCreateService`
821
838
  # In other words: Every and all records created in my application will be tracked
822
- # I can believe that's is easy like this, I need a beer right now!
839
+ # I can believe that is easy like this, I need a beer right now!
823
840
  NiftyServices::BaseCreateService.register_callback(:after_success, :create_origin_for_record) do
824
841
  create_origin(@record, @options)
825
842
  end
@@ -859,7 +876,7 @@ end
859
876
 
860
877
  ### Rails <a name="frameworks-rails"></a>
861
878
 
862
- You need a very minimal setup to integrate with you 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.
879
+ 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.
863
880
 
864
881
  First thing to do is add `lib/` folder in `autoload` path, place the following in your `config/application.rb`
865
882
 
@@ -875,13 +892,13 @@ Second, create `lib/services` directory:
875
892
  `$ mkdir -p lib/services/v1/users`
876
893
 
877
894
  Next, configure:
878
- **Note**: See Configurations section below to see all available configs
879
895
 
880
896
  ```ruby
881
897
  NiftyServices.configure do |config|
882
898
  config.user_class = User
883
899
  end
884
900
  ```
901
+ **Note**: See [Configurations](#construction-configuration-construction) section to see all available configs
885
902
 
886
903
  Create your first service:
887
904
 
@@ -909,7 +926,7 @@ class UsersController < BaseController
909
926
  end
910
927
  ```
911
928
 
912
- This can be even better if you move response code to an helper:
929
+ This can be even better if you move response code to a helper:
913
930
 
914
931
  ```ruby
915
932
  # helpers/users_helper.rb
@@ -920,7 +937,7 @@ module UsersHelper
920
937
  generic_response_for_service(service, success_response)
921
938
  end
922
939
 
923
- # THIS IS GREAT, you can use this method to standartize ALL of your
940
+ # THIS IS GREAT, you can use this method to standardize ALL of your
924
941
  # endpoints responses, THIS IS SO FUCKING COOL!
925
942
  def generic_response_for_service(service, success_response)
926
943
  default_response = {
@@ -978,6 +995,230 @@ NiftyServices - Sinatra Sample
978
995
 
979
996
  ---
980
997
 
998
+ ## :pray: Basic Service Markups :raised_hands:
999
+
1000
+ Here, for your convenience and sanity all basic service structures for reference when you start a brand new Service.
1001
+ Most of time, the best way is to copy all content from each service described below and change according to your needs.
1002
+
1003
+ ### BaseCreateService Basic Markup
1004
+
1005
+ ```ruby
1006
+ class SomeCreateService < NiftyServices::BaseCreateService
1007
+
1008
+ # [Required]
1009
+ # remember that inside the Service you always can use
1010
+ # @record variable to access current record
1011
+ # and from outside (service instance):
1012
+ # service.record or service.record_type
1013
+ # eg:
1014
+ # record_type BlogPost
1015
+ # service.record # BlogPost.new(...)
1016
+ # service.blog_post # BlogPost.new(...)
1017
+ # service.record == service.blog_post # true
1018
+ # alias_name can be used to create a custom alias name
1019
+ # eg:
1020
+ # record_type BlogPost, alias_name: :post
1021
+ # service.record # BlogPost.new(...)
1022
+ # service.post # BlogPost.new(...)
1023
+ # service.record == service.post # true
1024
+
1025
+ record_type RecordType, alias_name: :my_custom_alias_name
1026
+
1027
+ private
1028
+ # [Required]
1029
+ # Always validate if @user can create the current record_type
1030
+ # If this method is not implemented a NotImplementedError exception will be raised
1031
+ def user_can_create_record?
1032
+ return forbidden_error!('errors.some_error') if (some_validation)
1033
+
1034
+ return bad_request_error!('errors.some_other_error') if (another_validation)
1035
+
1036
+ # remember to return true after all validations
1037
+ # if you don't return true Service will not be able to create the record
1038
+ return true
1039
+ end
1040
+
1041
+ # [Optional]
1042
+ # method called when save_error method call raises an exception
1043
+ # this ocurr for example with ActiveRecord objects
1044
+ # default: unprocessable_entity_error!(error)
1045
+ def on_save_record_error(error)
1046
+ logger.error(error)
1047
+ if error.is_a?(ActiveRecord::RecordNotUnique)
1048
+ return unprocessable_entity_error!(%s(posts.duplicate_record))
1049
+ end
1050
+ end
1051
+
1052
+ # [Optional]
1053
+ # determine wheter user will be validate as valid object before
1054
+ # record creation
1055
+ # (default: true)
1056
+ def validate_user?
1057
+ return true
1058
+ end
1059
+
1060
+ # [Optional]
1061
+ # custom scope for record, eg: @user.posts
1062
+ # default is nil
1063
+ def build_record_scope
1064
+ end
1065
+ end
1066
+ ```
1067
+
1068
+ ### BaseUpdateService Basic Markup
1069
+
1070
+ ```ruby
1071
+ class SomeUpdateService < NiftyServices::BaseUpdateService
1072
+
1073
+ # [Required]
1074
+ record_type RecordType, alias_name: :custom_alias_name
1075
+
1076
+ WHITELIST_ATTRIBUTES = [
1077
+ :safe_attribute_1,
1078
+ :safe_attribute_2,
1079
+ ]
1080
+
1081
+ private
1082
+ # [Required]
1083
+ # When a new instance of Service is created, the @options variables receive some
1084
+ # values, eg: { user: { email: "...", name: "...."} }
1085
+ # use record_attributes_hash to tell the Service from where to pull theses values
1086
+ # eg: @options.fetch(:user, {})
1087
+ # If this method is not implemented a NotImplementedError exception will be raised
1088
+ def record_attributes_hash
1089
+ @options.fetch(options_key, {})
1090
+ end
1091
+
1092
+ # [Required]
1093
+ # whitelisted attributes (must be an Array) which can be updated by this Service
1094
+ # If this method is not implemented a NotImplementedError exception will be raised
1095
+ def record_attributes_whitelist
1096
+ WHITELIST_ATTRIBUTES
1097
+ end
1098
+
1099
+ # [required]
1100
+ # This is a VERY IMPORTANT point of attention
1101
+ # always verify if @user has permissions to update the current @record object
1102
+ # Hint: if @record respond_to `user_can_update?(user)` you can remove this
1103
+ # method and do the validation inside `user_can_update(user)` method in @record
1104
+ # If this method is not implemented a NotImplementedError exception will be raised
1105
+ def user_can_update_record?
1106
+ @record.user_id == @user.id
1107
+ end
1108
+
1109
+
1110
+ # [Optional]
1111
+ # This is the default implementation of update record, you may overwrite it
1112
+ # to to custom updates (MOST OF TIME YOU DONT NEED TO DO THIS)
1113
+ # only change this if you know what you are really doing
1114
+ def update_record
1115
+ @record.class.send(:update, @record.id, record_allowed_attributes)
1116
+ end
1117
+
1118
+
1119
+ # [optional]
1120
+ # Any callback is optional, this is just a example
1121
+ def after_success
1122
+ if changed?
1123
+ logger.info 'Successfully update record ID %s' % @record.id
1124
+ logger.info 'Changed attributes are %s' % changed_attributes
1125
+ end
1126
+ end
1127
+ end
1128
+ ```
1129
+
1130
+ ---
1131
+
1132
+ ### BaseDeleteService Basic Markup
1133
+
1134
+ ```ruby
1135
+ class SomeDeleteService < NiftyServices::BaseDeleteService
1136
+
1137
+ # [Required]
1138
+ record_type RecordType, alias_name: :custom_alias_name
1139
+
1140
+ private
1141
+
1142
+ # [Required]
1143
+ # This is a VERY IMPORTANT point of attention
1144
+ # always verify if @user has permissions to delete the current @record object
1145
+ # Hint: if @record respond_to `user_can_delete?(user)` you can remove this
1146
+ # method and do the validation inside `user_can_delete(user)` method in @record
1147
+ # If this method is not implemented a NotImplementedError exception will be raised
1148
+
1149
+ def user_can_delete_record?
1150
+ @record.user_id == @user.id
1151
+ end
1152
+
1153
+ # [optional]
1154
+ # Any callback is optional, this is just a example
1155
+ def after_success
1156
+ logger.info('Successfully Deleted resource ID %s' % @record.id)
1157
+ end
1158
+
1159
+ # [Optional]
1160
+ # This is the default implementation of delete record, you may overwrite it
1161
+ # to do custom delete (MOST OF TIME YOU DONT NEED TO DO THIS)
1162
+ # only change this if you know what you are really doing
1163
+ def destroy_record
1164
+ @record.try(:destroy) || @record.try(:delete)
1165
+ end
1166
+
1167
+ end
1168
+
1169
+ ```
1170
+
1171
+ ### BaseActionService Basic Markup
1172
+
1173
+ ```ruby
1174
+ class SomeCustomActionService < NiftyServices::BaseActionService
1175
+
1176
+ # [required]
1177
+ # this is the action identifier used internally
1178
+ # and to generate error messages
1179
+ # see: invalid_action_error_key method
1180
+ action_name :custom_action_name
1181
+
1182
+ private
1183
+ # [Required]
1184
+ # Always validate if Service can execute the action
1185
+ # This method MUST return a boolean value indicating if Service can or not
1186
+ # run the method `execute_service_action`
1187
+ # If this method is not implemented a NotImplementedError exception will be raised
1188
+ def user_can_execute_action?
1189
+ # do some specific validation here, you can return errors such:
1190
+ # return not_found_error!(%(users.invalid_user)) # returns false and avoid execution
1191
+ return true
1192
+ end
1193
+
1194
+ # [Required]
1195
+ # The core function of BaseActionServices
1196
+ # This method is called when all validations passes, so here you can put
1197
+ # all logic for Service (eg: send mails, clear logs, any kind of action you want)
1198
+ # If this method is not implemented a NotImplementedError exception will be raised
1199
+ def execute_service_action
1200
+ # (do some complex stuff)
1201
+ end
1202
+
1203
+ # You dont need to overwrite this method, just `record_error_key`
1204
+ # But it's important you know how final message key will be created
1205
+ # using the pattern below
1206
+ def invalid_action_error_key
1207
+ "#{record_error_key}.cant_execute_#{action_name}"
1208
+ end
1209
+
1210
+ # [Required]
1211
+ # Key used to created the error messages for this Service
1212
+ # If this method is not implemented a NotImplementedError exception will be raised
1213
+ def record_error_key
1214
+ :users
1215
+ end
1216
+ end
1217
+ ```
1218
+
1219
+
1220
+ ---
1221
+
981
1222
  ## Full Public API methods list
982
1223
 
983
1224
  You can use any of the methods above with your `services instances`:
@@ -1016,7 +1257,7 @@ Currently NiftyServices don't have CLI(command line interface) generators, but i
1016
1257
 
1017
1258
  - :white_medium_small_square: Remove ActiveSupport dependency
1018
1259
  - :white_medium_small_square: Create CLI Generators
1019
- - :white_medium_small_square: Document `BaseActionService`
1260
+ - :white_medium_small_square: Beter documentation for `BaseActionService`
1020
1261
  - :white_medium_small_square: Write Sample Applications
1021
1262
  - :white_medium_small_square: Write better tests for all `Crud Services`
1022
1263
  - :white_medium_small_square: Write better tests for `BaseActionServices`
@@ -8,12 +8,12 @@ module NiftyServices
8
8
  end
9
9
 
10
10
  def execute
11
- with_before_and_after_callbacks(:action) do
12
- if can_execute_action?
11
+ execute_action do
12
+ with_before_and_after_callbacks(:action) do
13
+ # here user can
14
+ execute_service_action
13
15
 
14
- execute_action
15
-
16
- if success_runned_action?
16
+ if valid?
17
17
  success_response
18
18
  else
19
19
  errors = action_errors
@@ -21,8 +21,6 @@ module NiftyServices
21
21
  end
22
22
  end
23
23
  end
24
-
25
- success?
26
24
  end
27
25
 
28
26
  private
@@ -30,50 +28,31 @@ module NiftyServices
30
28
  []
31
29
  end
32
30
 
33
- def can_execute_action?
34
- unless valid_record?
35
- return not_found_error!("#{record_error_key}.not_found")
36
- end
37
-
38
- unless valid_user?
39
- return not_found_error!('users.not_found')
40
- end
41
-
42
- unless can_execute?
43
- if @errors.blank?
44
- return unprocessable_entity_error!("#{record_error_key}.user_cant_execute_#{action_name}")
45
- else
46
- return false
47
- end
31
+ def can_execute?
32
+ unless user_can_execute_action?
33
+ return (valid? ? unprocessable_entity_error!(invalid_action_error_key) : false)
48
34
  end
49
35
 
50
36
  return true
51
37
  end
52
38
 
53
- def can_execute?
54
- return false unless valid_user?
55
- return false unless valid_record?
56
-
57
- user_can_execute_action?
58
- end
59
-
60
- def success_runned_action?
61
- not_implemented_exception(__method__)
39
+ def invalid_action_error_key
40
+ "#{record_error_key}.cant_execute_#{action_name}"
62
41
  end
63
42
 
64
43
  def user_can_execute_action?
65
44
  not_implemented_exception(__method__)
66
45
  end
67
46
 
68
- def execute_action
47
+ def execute_service_action
69
48
  not_implemented_exception(__method__)
70
49
  end
71
50
 
72
- def valid_record?
51
+ def record_error_key
73
52
  not_implemented_exception(__method__)
74
53
  end
75
54
 
76
- def record_error_key
55
+ def action_name
77
56
  not_implemented_exception(__method__)
78
57
  end
79
58
  end
@@ -25,7 +25,16 @@ module NiftyServices
25
25
 
26
26
  private
27
27
  def save_record
28
- @record.save
28
+ begin
29
+ @record.save
30
+ rescue => e
31
+ on_save_record_error(e)
32
+ return false
33
+ end
34
+ end
35
+
36
+ def on_save_record_error(error)
37
+ return unprocessable_entity_error!(error)
29
38
  end
30
39
 
31
40
  def create_error_response(record)
@@ -40,21 +49,20 @@ module NiftyServices
40
49
  success_created_response
41
50
  end
42
51
 
43
- def after_success
44
- end
45
-
46
52
  def build_record
47
- _record_params = record_allowed_params
48
-
49
- if record_type.present? && _record_params.present?
50
- return build_from_record_type(_record_params)
53
+ if record_type.present?
54
+ return build_from_record_type(record_allowed_attributes)
51
55
  end
52
56
 
53
57
  return not_implemented_exception(__method__)
54
58
  end
55
59
 
56
- def build_from_record_type(record_params)
57
- record_type.send(:new, record_params)
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)
58
66
  end
59
67
 
60
68
  def can_execute?
@@ -62,25 +70,27 @@ module NiftyServices
62
70
  return forbidden_error!(%s(users.ip_temporarily_blocked))
63
71
  end
64
72
 
65
- unless valid_user?
66
- return not_found_error!(%s(users.not_found))
73
+ if validate_user? && !valid_user?
74
+ return not_found_error!(invalid_user_error_key)
67
75
  end
68
76
 
69
- unless can_create?
70
- if @errors.blank?
71
- return forbidden_error!("#{record_error_key}.user_cant_create")
72
- else
73
- return false
74
- end
77
+ return true
78
+ end
79
+
80
+ def can_create_record?
81
+ unless user_can_create_record?
82
+ return (valid? ? forbidden_error!(user_cant_create_error_key) : false)
75
83
  end
76
84
 
77
- return valid?
85
+ return true
78
86
  end
79
87
 
80
- def can_create?
81
- return false unless valid_user?
88
+ def user_can_create_record?
89
+ not_implemented_exception(__method__)
90
+ end
82
91
 
83
- return user_can_create_record?
92
+ def can_execute_action?
93
+ return can_create_record?
84
94
  end
85
95
 
86
96
  def can_create_with_ip?
@@ -92,8 +102,12 @@ module NiftyServices
92
102
  false
93
103
  end
94
104
 
95
- def user_can_create_record?
96
- not_implemented_exception(__method__)
105
+ def build_record_scope
106
+ nil
107
+ end
108
+
109
+ def user_cant_create_error_key
110
+ "#{record_error_key}.user_cant_create"
97
111
  end
98
112
  end
99
113
  end
@@ -37,28 +37,31 @@ module NiftyServices
37
37
  super(options)
38
38
  end
39
39
 
40
- def execute
41
- not_implemented_exception(__method__)
40
+ def changed_attributes
41
+ []
42
+ end
43
+
44
+ def changed?
45
+ changed_attributes.any?
42
46
  end
43
47
 
44
48
  def record_type
45
49
  not_implemented_exception(__method__)
46
50
  end
47
51
 
48
- def record_params
52
+ def record_attributes_hash
49
53
  not_implemented_exception(__method__)
50
54
  end
51
55
 
52
- def record_params_whitelist
56
+ def record_attributes_whitelist
53
57
  not_implemented_exception(__method__)
54
58
  end
55
59
 
56
- def record_allowed_params
57
- filter_hash(record_params, record_params_whitelist)
60
+ def record_allowed_attributes
61
+ filter_hash(record_attributes_hash, record_attributes_whitelist)
58
62
  end
59
63
 
60
- alias :record_whitelisted_params :record_allowed_params
61
- alias :record_safe_params :record_allowed_params
64
+ alias :record_safe_attributes :record_allowed_attributes
62
65
 
63
66
  private
64
67
  def array_values_from_hash(options, key, root = nil)
@@ -77,16 +80,20 @@ module NiftyServices
77
80
  array_values_from_string(values)
78
81
  end
79
82
 
83
+ def invalid_user_error_key
84
+ %s(users.not_found)
85
+ end
86
+
87
+ def validate_user?
88
+ true
89
+ end
90
+
80
91
  alias :array_values_from_params :array_values_from_hash
81
92
 
82
93
  def array_values_from_string(string)
83
94
  string.to_s.split(/\,/).map(&:squish)
84
95
  end
85
96
 
86
- def record_params
87
- not_implemented_exception(__method__)
88
- end
89
-
90
97
  def record_error_key
91
98
  record_type.to_s.pluralize.underscore
92
99
  end
@@ -20,31 +20,38 @@ module NiftyServices
20
20
  @record.try(:destroy) || @record.try(:delete)
21
21
  end
22
22
 
23
- def can_execute_action?
23
+ def can_execute?
24
24
  unless valid_record?
25
25
  return not_found_error!("#{record_error_key}.not_found")
26
26
  end
27
27
 
28
- unless valid_user?
29
- return not_found_error!('users.not_found')
28
+ if validate_user? && !valid_user?
29
+ return not_found_error!(invalid_user_error_key)
30
30
  end
31
31
 
32
- unless can_delete?
33
- return forbidden_error!("#{record_error_key}.user_cant_delete")
32
+ return true
33
+ end
34
+
35
+ def can_delete_record?
36
+ unless user_can_delete_record?
37
+ return (valid? ? forbidden_error!(user_can_delete_error_key) : false)
34
38
  end
35
39
 
36
40
  return true
37
41
  end
38
42
 
39
- def can_delete?
40
- return false unless valid_user?
41
- return false unless valid_record?
42
-
43
- return user_can_delete_record?
43
+ def can_execute_action?
44
+ return can_delete_record?
44
45
  end
45
46
 
46
47
  def user_can_delete_record?
48
+ return not_implemented_exception(__method__) unless @record.respond_to?(:user_can_delete?)
49
+
47
50
  @record.user_can_delete?(@user)
48
51
  end
52
+
53
+ def user_cant_delete_error_key
54
+ "#{record_error_key}.user_cant_delete"
55
+ end
49
56
  end
50
57
  end
@@ -26,7 +26,7 @@ module NiftyServices
26
26
  end
27
27
 
28
28
  def initialize(options = {}, initial_response_status = 400)
29
- @options = options.to_options!
29
+ @options = default_options.to_options!.merge(options).to_options!
30
30
  @errors = []
31
31
  @logger = options[:logger] || default_logger
32
32
  @executed = false
@@ -36,6 +36,10 @@ module NiftyServices
36
36
  end
37
37
  end
38
38
 
39
+ def execute
40
+ not_implemented_exception(__method__)
41
+ end
42
+
39
43
  def valid?
40
44
  return @errors.blank?
41
45
  end
@@ -52,14 +56,6 @@ module NiftyServices
52
56
  @response_status ||= :bad_request
53
57
  end
54
58
 
55
- def changed_attributes
56
- []
57
- end
58
-
59
- def changed?
60
- changed_attributes.any?
61
- end
62
-
63
59
  def valid_user?
64
60
  user_class = NiftyServices.config.user_class
65
61
 
@@ -98,20 +94,28 @@ module NiftyServices
98
94
  alias :runned? :executed?
99
95
 
100
96
  private
97
+ def default_options
98
+ {}
99
+ end
100
+
101
101
  def can_execute?
102
102
  not_implemented_exception(__method__)
103
103
  end
104
104
 
105
105
  def execute_action(&block)
106
- return nil if executed?
106
+ begin
107
+ return nil if executed?
107
108
 
108
- with_before_and_after_callbacks(:execute) do
109
- if can_execute?
110
- yield(block) if block_given?
109
+ with_before_and_after_callbacks(:execute) do
110
+ if can_execute?
111
+ yield(block) if block_given?
112
+ end
111
113
  end
112
- end
113
114
 
114
- @executed = true
115
+ @executed = true
116
+ rescue Exception => e
117
+ add_error(e)
118
+ end
115
119
 
116
120
  self # allow chaining
117
121
  end
@@ -203,7 +207,7 @@ module NiftyServices
203
207
  changes << attribute if (old_attributes[attribute] != value)
204
208
  end
205
209
 
206
- changes
210
+ changes.map(&:to_sym)
207
211
  end
208
212
 
209
213
  def i18n_namespace
@@ -5,8 +5,9 @@ module NiftyServices
5
5
  execute_action do
6
6
  with_before_and_after_callbacks(:update) do
7
7
  if can_execute_action?
8
- @updated_record = update_record
9
- @updated_record ||= @record
8
+ duplicate_records_before_update
9
+
10
+ @record = update_record
10
11
 
11
12
  if success_updated?
12
13
  success_response
@@ -21,58 +22,68 @@ module NiftyServices
21
22
 
22
23
  def changed_attributes
23
24
  return [] if fail?
24
- @changed_attributes ||= changes(@old_record, @record, changed_attributes_array)
25
+ @changed_attributes ||= changes(@last_record, @record, changed_attributes_array)
25
26
  end
26
27
 
27
28
  private
28
29
 
29
30
  def changed_attributes_array
30
- record_allowed_params.keys
31
+ record_allowed_attributes.keys
31
32
  end
32
33
 
33
34
  def success_updated?
34
- @updated_record.valid?
35
+ @record.valid?
35
36
  end
36
37
 
37
38
  def update_errors
38
- @updated_record.errors
39
+ @record.errors
39
40
  end
40
41
 
41
42
  def update_record
42
- @record.class.send(:update, @record.id, record_allowed_params)
43
+ @record.class.send(:update, @record.id, record_allowed_attributes)
43
44
  end
44
45
 
45
- def can_execute_action?
46
-
46
+ def can_execute?
47
47
  unless valid_record?
48
- return not_found_error!("#{record_error_key}.not_found")
48
+ return not_found_error!(invalid_record_error_key)
49
49
  end
50
50
 
51
- unless valid_user?
52
- return not_found_error!('users.not_found')
51
+ if validate_user? && !valid_user?
52
+ return not_found_error!(invalid_user_error_key)
53
53
  end
54
54
 
55
- unless can_update?
56
- return forbidden_error!("#{record_error_key}.user_cant_update")
55
+ return true
56
+ end
57
+
58
+ def can_update_record?
59
+ unless user_can_update_record?
60
+ return (valid? ? forbidden_error!(user_cant_update_error_key) : false)
57
61
  end
58
62
 
59
63
  return true
60
64
  end
61
65
 
62
- def can_update?
63
- return false unless valid_user?
64
- return false unless valid_record?
65
-
66
- return user_can_update_record?
66
+ def can_execute_action?
67
+ return can_update_record?
67
68
  end
68
69
 
69
70
  def user_can_update_record?
71
+ return not_implemented_exception(__method__) unless @record.respond_to?(:user_can_update?)
72
+
70
73
  @record.user_can_update?(@user)
71
74
  end
72
75
 
73
- def after_success
74
- @old_record = @record.dup
75
- @record = @updated_record
76
+ def duplicate_records_before_update
77
+ @last_record = @record.dup
78
+ end
79
+
80
+ def invalid_record_error_key
81
+ "#{record_error_key}.not_found"
76
82
  end
83
+
84
+ def user_cant_update_error_key
85
+ "#{record_error_key}.user_cant_update"
86
+ end
87
+
77
88
  end
78
89
  end
@@ -31,9 +31,9 @@ module NiftyServices
31
31
  end
32
32
  end
33
33
 
34
- attr_reader :logger, :options, :i18n_namespace
34
+ attr_reader :options
35
35
 
36
- attr_accessor :user_class, :service_concerns_namespace
36
+ attr_accessor :logger, :i18n_namespace, :user_class, :service_concerns_namespace
37
37
 
38
38
  def initialize(options = {})
39
39
  @options = options
@@ -1,3 +1,3 @@
1
1
  module NiftyServices
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -20,10 +20,9 @@ Gem::Specification.new do |spec|
20
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
21
  spec.require_paths = ["lib"]
22
22
 
23
- spec.add_dependency 'activesupport', '~> 4.2.2'
24
-
25
- spec.add_development_dependency "bundler", "~> 1.10"
26
- spec.add_development_dependency "rake", "~> 10.0"
27
- spec.add_development_dependency "rspec"
28
- spec.add_development_dependency "pry"
23
+ spec.add_development_dependency "bundler"
24
+ spec.add_development_dependency "rake", '~> 10.5.0'
25
+ spec.add_development_dependency "rspec", '~> 3.5.0'
26
+ spec.add_development_dependency "pry", '~> 0'
27
+ spec.add_runtime_dependency 'activesupport', '>= 4.2.2'
29
28
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nifty_services
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rafael Fidelis
@@ -11,75 +11,75 @@ cert_chain: []
11
11
  date: 2016-07-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: activesupport
14
+ name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 4.2.2
20
- type: :runtime
19
+ version: '0'
20
+ type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 4.2.2
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: bundler
28
+ name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.10'
33
+ version: 10.5.0
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.10'
40
+ version: 10.5.0
41
41
  - !ruby/object:Gem::Dependency
42
- name: rake
42
+ name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '10.0'
47
+ version: 3.5.0
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '10.0'
54
+ version: 3.5.0
55
55
  - !ruby/object:Gem::Dependency
56
- name: rspec
56
+ name: pry
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: pry
70
+ name: activesupport
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
75
+ version: 4.2.2
76
+ type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
- version: '0'
82
+ version: 4.2.2
83
83
  description: The dead simple services object oriented layer for Ruby applications
84
84
  to give robustness and cohesion back to your code.
85
85
  email:
@@ -99,7 +99,6 @@ files:
99
99
  - Rakefile
100
100
  - bin/console
101
101
  - bin/setup
102
- - daily_news.log
103
102
  - lib/nifty_services.rb
104
103
  - lib/nifty_services/base_action_service.rb
105
104
  - lib/nifty_services/base_create_service.rb
@@ -113,7 +112,6 @@ files:
113
112
  - lib/nifty_services/util.rb
114
113
  - lib/nifty_services/version.rb
115
114
  - nifty_services.gemspec
116
- - test.rb
117
115
  homepage: https://github.com/fidelisrafael/nifty_services
118
116
  licenses:
119
117
  - MIT
data/daily_news.log DELETED
@@ -1,7 +0,0 @@
1
- # Logfile created on 2016-07-15 17:46:01 -0300 by logger.rb/53141
2
- I, [2016-07-15T17:46:01.453040 #30030] INFO -- : Routine Details: Send daily news email to user Rafael Fidelis(rafa_fidelis@yahoo.com.br)
3
- I, [2016-07-15T17:46:01.453179 #30030] INFO -- : Routine started at: 2016-07-15 17:46:01 -0300
4
- W, [2016-07-15T17:46:01.453464 #30030] WARN -- : Something went wrong
5
- E, [2016-07-15T17:46:01.484243 #30030] ERROR -- : Error sending email to user. See details below :(
6
- E, [2016-07-15T17:46:01.484330 #30030] ERROR -- : ["translation missing: en.nifty_services.errors.users.yet_received_daily_news_mail"]
7
- I, [2016-07-15T17:46:01.484470 #30030] INFO -- : Routine ended at: 2016-07-15 17:46:01 -0300
data/test.rb DELETED
@@ -1,82 +0,0 @@
1
- class DailyNewsMailSendService < NiftyServices::BaseService
2
-
3
- before_execute do
4
- log.info('Routine started at: %s' % Time.now)
5
- end
6
-
7
- after_execute do
8
- log.info('Routine ended at: %s' % Time.now)
9
- end
10
-
11
- after_initialize do
12
- user_data = [@user.name, @user.email]
13
- log.info('Routine Details: Send daily news email to user %s(%s)' % user_data)
14
- end
15
-
16
- after_success do
17
- log.info('Success sent daily news feed email to user')
18
- end
19
-
20
- before_error do
21
- log.warn('Something went wrong')
22
- end
23
-
24
- after_error do
25
- log.error('Error sending email to user. See details below :(')
26
- log.error(errors)
27
- end
28
-
29
- attr_reader :user
30
-
31
- def initialize(user, options = {})
32
- @user = user
33
- super(options)
34
- end
35
-
36
- def execute
37
- execute_action do
38
- if can_execute?
39
- success_response if send_mail_to_user
40
- end
41
- end
42
- end
43
-
44
- private
45
- def send_mail_to_user
46
- # just to fake, a real implementation could be something like:
47
- # @user.send_daily_news_mail!
48
- return true
49
- end
50
-
51
- def can_execute?
52
- unless valid_user?
53
- # returns false
54
- return not_found_error!('users.not_found')
55
- end
56
-
57
- unless @user.abble_to_receive_daily_news_mail?
58
- # returns false
59
- return forbidden_error!('users.yet_received_daily_news_mail')
60
- end
61
-
62
- return true
63
- end
64
-
65
- def valid_user?
66
- # check if object is valid and is a User class type
67
- valid_object?(@user, User)
68
- end
69
- end
70
-
71
- class User < Struct.new(:name, :email)
72
- # just to play around with results
73
- def abble_to_receive_daily_news_mail?
74
- rand(10) < 5
75
- end
76
- end
77
-
78
- user = User.new('Rafael Fidelis', 'rafa_fidelis@yahoo.com.br')
79
-
80
- # Default logger is NiftyService.config.logger # Logger.new('/dev/null')
81
- service = DailyNewsMailSendService.new(user, logger: Logger.new('daily_news.log'))
82
- service.execute