action_processor 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 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: []