action_processor 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4771896f3d4045a45c5b2913c6eecb27f17d9814f027ac6faa343fba0fb3cc04
4
+ data.tar.gz: 3212b5d960508dc66eb1ddfeb7b1f1bec9617d3bedfffda59e8332454af002e8
5
+ SHA512:
6
+ metadata.gz: b277de0643b7abff6121730e1e44fe5a556bc5e0d303c3408c0b39fcffaa5365ff9505632a67a37495662acc386484b8d96f6bdaa50d721d7076bd53ad89bd41
7
+ data.tar.gz: c3ed077263b9ab97812052e99d4288de3236b02182f19b98a396375b6eb35760f38c75fe64bea59ba99a57673c92ab4287f7cf9add4c25b54c23e449f69615d4
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/indifferent_access"
4
+ require "active_record"
5
+
6
+ module ActionProcessor
7
+ class Base
8
+ attr_reader :params, :errors
9
+
10
+ def self.run(params = {})
11
+ inst = new(params)
12
+ inst.run
13
+ inst
14
+ end
15
+
16
+ def initialize(params = {})
17
+ @params = if params.class.name == 'Hash'
18
+ params.with_indifferent_access
19
+ else
20
+ params
21
+ end
22
+ @errors = ActionProcessor::Errors.new
23
+ @steps_stack = []
24
+ @transaction_level = ActiveRecord::Base.connection.open_transactions
25
+ rescue ActiveRecord::ConnectionNotEstablished
26
+ @transaction_level = nil
27
+ end
28
+
29
+ def run
30
+ raise 'Error: method "run" should be overridden to implement business logic!'
31
+ end
32
+
33
+ def errors?
34
+ @errors.all.any?
35
+ end
36
+
37
+ def success?
38
+ @errors.all.empty?
39
+ end
40
+
41
+ def json_outcome
42
+ errors? ? failed_json : successful_json
43
+ end
44
+
45
+ # in most cases should be overriden to provide relevant data
46
+ def successful_json
47
+ { success: true }
48
+ end
49
+
50
+ # could be overriden to provide some specifics/advices/etc.
51
+ def failed_json
52
+ { success: false, errors: errors.grouped_by_attribute }
53
+ end
54
+
55
+ def step(step_method, **options)
56
+ return if errors? # skip it if there are errors
57
+
58
+ step_always(step_method, **options)
59
+ end
60
+
61
+ def step_always(step_method, **options)
62
+ @steps_stack << (@current_step || :not_specified)
63
+ @current_step = step_method
64
+ # performs even if there are errors
65
+ # useful for:
66
+ # - validation steps to return list of all errors
67
+ # - errors reporting and making decisions at the end of processing
68
+ send step_method, **options
69
+ @current_step = @steps_stack.pop
70
+ end
71
+
72
+ # As an "errs" params we could pass several types:
73
+ # - String with error description
74
+ # - array of Strings (error messages)
75
+ # - ActiveRecord model (invalid) - its errors will be copied
76
+ def fail!(errs, attr = :not_specified)
77
+ if errs.class.ancestors.map(&:to_s).include?("ActiveRecord::Base")
78
+ fail_active_record!(errs)
79
+ else
80
+ @errors.add(errs, @current_step, attr)
81
+ end
82
+
83
+ raise ActiveRecord::Rollback if in_transaction?
84
+ end
85
+
86
+ private
87
+
88
+ # simple params presence validation
89
+ # will initiate errors for each absent params
90
+ def required_params(*list)
91
+ list.flatten!
92
+ @list_of_allowed_params ||= []
93
+ @list_of_allowed_params += list.map(&:to_s)
94
+ list.map(&:to_s).each do |param|
95
+ next if params[param].present?
96
+
97
+ fail! "Parameter :#{param} should be present", param
98
+ end
99
+ end
100
+
101
+ # allowed params (in addition to required_params)
102
+ # if allowed_params present method called, all params
103
+ # provided to Processor which are not included in
104
+ # lists specified for required_params and allowed_params
105
+ # will cause validation error
106
+ def allowed_params(*list)
107
+ list.flatten!
108
+ @list_of_allowed_params ||= []
109
+ @list_of_allowed_params += list.map(&:to_s)
110
+
111
+ params.each do |key, value|
112
+ next if @list_of_allowed_params.index(key.to_s).present?
113
+
114
+ fail! "Parameter :#{key} is not allowed", key.to_sym
115
+ end
116
+ end
117
+
118
+ def fail_active_record!(record)
119
+ record.errors.each do |err|
120
+ fail! "#{err.attribute.to_s.humanize} #{err.message}", err.attribute
121
+ end
122
+ end
123
+
124
+ def in_transaction?
125
+ return false if @transaction_level.nil?
126
+
127
+ ActiveRecord::Base.connection.open_transactions > @transaction_level
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/indifferent_access"
4
+
5
+ module ActionProcessor
6
+ class Errors
7
+ attr_reader :all
8
+
9
+ def initialize
10
+ @all = []
11
+ end
12
+
13
+ def add(messages, step = :not_specified, attribute = :not_specified)
14
+ step ||= :not_specified # in case we will receive explicit nil as step parameter
15
+ @all << { messages: [messages].flatten, step: step.to_sym, attribute: attribute.to_sym }
16
+ end
17
+
18
+ # returns array of strings with user friendly error messages
19
+ def messages
20
+ all_messages = []
21
+ @all.each do |e|
22
+ all_messages += e[:messages]
23
+ end
24
+ all_messages
25
+ end
26
+
27
+ alias full_messages messages # for compatibility with ActiveRecord::Errors
28
+
29
+ def for_attribute(attr)
30
+ @grouped_by_attribute[attr]
31
+ end
32
+
33
+ def grouped_by_attribute
34
+ return @grouped_by_attribute if @grouped_by_attribute.present?
35
+
36
+ # we assume that all errors will be present
37
+ # at the time when this method called first time
38
+ @grouped_by_attribute = {}.with_indifferent_access
39
+ @all.each do |err|
40
+ @grouped_by_attribute[err.attribute] ||= []
41
+ @grouped_by_attribute[err.attribute] += err[:messages]
42
+ end
43
+ @grouped_by_attribute
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,2 @@
1
+ require_relative "./action_processor/errors.rb"
2
+ require_relative "./action_processor/base.rb"
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: action_processor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.6
5
+ platform: ruby
6
+ authors:
7
+ - Ghennadii Mirosnicenco
8
+ - Pavel Mirosnicenco
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2023-02-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 7.0.0
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 7.0.0
28
+ - !ruby/object:Gem::Dependency
29
+ name: activerecord
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: 7.0.0
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: 7.0.0
42
+ description: action_processor
43
+ email: linkator7@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - lib/action_processor.rb
49
+ - lib/action_processor/base.rb
50
+ - lib/action_processor/errors.rb
51
+ homepage: https://github.com/GhennadiiMir/action_processor
52
+ licenses:
53
+ - MIT
54
+ metadata: {}
55
+ post_install_message:
56
+ rdoc_options: []
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '3'
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ requirements: []
70
+ rubygems_version: 3.3.7
71
+ signing_key:
72
+ specification_version: 4
73
+ summary: Group each of your complex multy-model manipulations in dedicated ActionProcessor
74
+ test_files: []