├── src
├── package.devc.xml
├── zcl_result.clas.xml
├── zcx_result_is_not_ok.clas.xml
├── zcx_result_is_not_failure.clas.xml
├── zcx_result_is_not_ok.clas.abap
├── zcx_result_is_not_failure.clas.abap
├── zcl_result.clas.abap
└── zcl_result.clas.testclasses.abap
├── .github
└── workflows
│ ├── lint.yml
│ └── unittests.yml
├── abap_transpile.json
├── .abapgit.xml
├── package.json
├── abaplint-downport.json
├── LICENSE
├── abaplint.json
└── README.md
/src/package.devc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Result Pattern
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: Run abaplint
2 | on:
3 | push:
4 | branches:
5 | - main
6 | pull_request:
7 | branches:
8 | - main
9 |
10 | jobs:
11 | lint:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v2
15 | - uses: actions/setup-node@v2
16 | with:
17 | node-version: '16'
18 | - name: npm run unit
19 | run: |
20 | npm install
21 | npm run abaplint
22 |
--------------------------------------------------------------------------------
/.github/workflows/unittests.yml:
--------------------------------------------------------------------------------
1 | name: Run Unit Tests
2 | on:
3 | push:
4 | branches:
5 | - main
6 | pull_request:
7 | branches:
8 | - main
9 |
10 | jobs:
11 | lint:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v2
15 | - uses: actions/setup-node@v2
16 | with:
17 | node-version: '16'
18 | - name: npm run unit
19 | run: |
20 | npm install
21 | npm run unit
22 |
--------------------------------------------------------------------------------
/abap_transpile.json:
--------------------------------------------------------------------------------
1 | {
2 | "input_folder": "downport",
3 | "input_filter": [],
4 | "output_folder": "output",
5 | "libs": [
6 | {
7 | "url": "https://github.com/open-abap/open-abap"
8 | }
9 | ],
10 | "write_unit_tests": true,
11 | "write_source_map": true,
12 | "options": {
13 | "ignoreSyntaxCheck": true,
14 | "addFilenames": true,
15 | "addCommonJS": true,
16 | "unknownTypes": "runtimeError",
17 | "skip": []
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/zcl_result.clas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZCL_RESULT
7 | E
8 | Result
9 | 1
10 | X
11 | X
12 | X
13 | X
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.abapgit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | E
6 | /src/
7 | FULL
8 |
9 | - /.gitignore
10 | - /LICENSE
11 | - /README.md
12 | - /package.json
13 | - /.travis.yml
14 | - /.gitlab-ci.yml
15 | - /abaplint.json
16 | - /azure-pipelines.yml
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "license": "MIT",
3 | "private": true,
4 | "scripts": {
5 | "unit": "npm run downport && rm -rf output && abap_transpile abap_transpile.json && echo RUNNING && node output/index.mjs",
6 | "downport": "rm -rf downport && cp -r src downport && abaplint --fix abaplint-downport.json",
7 | "test": "npm run unit",
8 | "abaplint": "abaplint --format codeframe"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/dominikpanzer/RESULT-for-ABAP.git"
13 | },
14 | "devDependencies": {
15 | "@abaplint/cli": "^2.97.9",
16 | "@abaplint/transpiler-cli": "^2.6.15",
17 | "@abaplint/runtime": "^2.6.15"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/abaplint-downport.json:
--------------------------------------------------------------------------------
1 | {
2 | "global": {
3 | "files": "/downport/**/*.*"
4 | },
5 | "dependencies": [
6 | {
7 | "url": "https://github.com/open-abap/open-abap",
8 | "files": "/src/**/*.*"
9 | }
10 | ],
11 | "syntax": {
12 | "version": "v702",
13 | "errorNamespace": "."
14 | },
15 | "rules": {
16 | "downport": true,
17 |
18 | "begin_end_names": true,
19 | "check_ddic": true,
20 | "check_include": true,
21 | "check_syntax": true,
22 | "global_class": true,
23 | "implement_methods": true,
24 | "definitions_top": true,
25 | "method_implemented_twice": true,
26 | "parser_error": true,
27 | "parser_missing_space": true,
28 | "superclass_final": true,
29 | "unknown_types": true,
30 | "xml_consistency": true
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/zcx_result_is_not_ok.clas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZCX_RESULT_IS_NOT_OK
7 | E
8 | Result is not ok
9 | 40
10 | 1
11 | X
12 | X
13 | X
14 |
15 |
16 |
17 | ZCX_RESULT_IS_NOT_OK
18 | CONSTRUCTOR
19 | E
20 | CONSTRUCTOR
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/zcx_result_is_not_failure.clas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZCX_RESULT_IS_NOT_FAILURE
7 | E
8 | Result is no failure
9 | 40
10 | 1
11 | X
12 | X
13 | X
14 |
15 |
16 |
17 | ZCX_RESULT_IS_NOT_FAILURE
18 | CONSTRUCTOR
19 | E
20 | CONSTRUCTOR
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/zcx_result_is_not_ok.clas.abap:
--------------------------------------------------------------------------------
1 | CLASS zcx_result_is_not_ok DEFINITION
2 | PUBLIC
3 | INHERITING FROM cx_no_check
4 | FINAL
5 | CREATE PUBLIC .
6 |
7 | PUBLIC SECTION.
8 |
9 | INTERFACES if_t100_message .
10 | INTERFACES if_t100_dyn_msg .
11 |
12 | METHODS constructor
13 | IMPORTING
14 | !textid LIKE if_t100_message=>t100key OPTIONAL
15 | !previous LIKE previous OPTIONAL .
16 | PROTECTED SECTION.
17 | PRIVATE SECTION.
18 | ENDCLASS.
19 |
20 |
21 |
22 | CLASS zcx_result_is_not_ok IMPLEMENTATION.
23 |
24 |
25 | METHOD constructor ##ADT_SUPPRESS_GENERATION.
26 | CALL METHOD super->constructor
27 | EXPORTING
28 | previous = previous.
29 | CLEAR me->textid.
30 | IF textid IS INITIAL.
31 | if_t100_message~t100key = if_t100_message=>default_textid.
32 | ELSE.
33 | if_t100_message~t100key = textid.
34 | ENDIF.
35 | ENDMETHOD.
36 | ENDCLASS.
37 |
--------------------------------------------------------------------------------
/src/zcx_result_is_not_failure.clas.abap:
--------------------------------------------------------------------------------
1 | CLASS zcx_result_is_not_failure DEFINITION
2 | PUBLIC
3 | INHERITING FROM cx_no_check
4 | FINAL
5 | CREATE PUBLIC .
6 |
7 | PUBLIC SECTION.
8 |
9 | INTERFACES if_t100_message .
10 | INTERFACES if_t100_dyn_msg .
11 |
12 | METHODS constructor
13 | IMPORTING
14 | !textid LIKE if_t100_message=>t100key OPTIONAL
15 | !previous LIKE previous OPTIONAL .
16 | PROTECTED SECTION.
17 | PRIVATE SECTION.
18 | ENDCLASS.
19 |
20 |
21 |
22 | CLASS zcx_result_is_not_failure IMPLEMENTATION.
23 |
24 |
25 | METHOD constructor ##ADT_SUPPRESS_GENERATION.
26 | CALL METHOD super->constructor
27 | EXPORTING
28 | previous = previous.
29 | CLEAR me->textid.
30 | IF textid IS INITIAL.
31 | if_t100_message~t100key = if_t100_message=>default_textid.
32 | ELSE.
33 | if_t100_message~t100key = textid.
34 | ENDIF.
35 | ENDMETHOD.
36 | ENDCLASS.
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Dominik Panzer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/abaplint.json:
--------------------------------------------------------------------------------
1 | {
2 | "global": {
3 | "files": "/src/**/*.*",
4 | "exclude": [],
5 | "skipGeneratedGatewayClasses": true,
6 | "skipGeneratedPersistentClasses": true,
7 | "skipGeneratedFunctionGroups": true,
8 | "useApackDependencies": false,
9 | "skipIncludesWithoutMain": false
10 | },
11 | "dependencies": [
12 | {
13 | "url": "https://github.com/open-abap/open-abap-core",
14 | "folder": "/open-abap",
15 | "files": "/src/**/*.*"
16 | },
17 | {
18 | "folder": "/deps",
19 | "files": "/**/*.*"
20 | }
21 | ],
22 | "syntax": {
23 | "version": "Cloud",
24 | "errorNamespace": ".",
25 | "globalConstants": []
26 | },
27 | "rules": {
28 | "align_parameters": false,
29 | "cds_parser_error": true,
30 | "constant_classes": true,
31 | "no_aliases": true,
32 | "pragma_style": true,
33 | "unnecessary_pragma": true,
34 | "no_chained_assignment": true,
35 | "nrob_consistency": true,
36 | "omit_preceding_zeros": true,
37 | "prefer_corresponding": true,
38 | "slow_parameter_passing": true,
39 | "static_call_via_instance": true,
40 | "unnecessary_chaining": false,
41 | "7bit_ascii": true,
42 | "abapdoc": false,
43 | "allowed_object_naming": false,
44 | "allowed_object_types": true,
45 | "ambiguous_statement": true,
46 | "avoid_use": false,
47 | "begin_end_names": true,
48 | "begin_single_include": {
49 | "exclude": ["testclasses"]
50 | },
51 | "call_transaction_authority_check": true,
52 | "chain_mainly_declarations": true,
53 | "check_abstract": true,
54 | "check_comments": false,
55 | "check_ddic": true,
56 | "check_include": true,
57 | "check_subrc": false,
58 | "check_syntax": true,
59 | "check_text_elements": true,
60 | "check_transformation_exists": true,
61 | "class_attribute_names": false,
62 | "cloud_types": true,
63 | "colon_missing_space": true,
64 | "commented_code": true,
65 | "constructor_visibility_public": false,
66 | "contains_tab": true,
67 | "cyclomatic_complexity": {
68 | "exclude": [],
69 | "max": 20
70 | },
71 | "dangerous_statement": false,
72 | "db_operation_in_loop": true,
73 | "definitions_top": false,
74 | "description_empty": false,
75 | "double_space": true,
76 | "downport": true,
77 | "empty_line_in_statement": false,
78 | "empty_statement": true,
79 | "empty_structure": true,
80 | "exit_or_check": false,
81 | "exporting": true,
82 | "forbidden_identifier": true,
83 | "forbidden_pseudo_and_pragma": true,
84 | "forbidden_void_type": true,
85 | "form_tables_obsolete": true,
86 | "fully_type_constants": true,
87 | "function_module_recommendations": true,
88 | "functional_writing": true,
89 | "global_class": true,
90 | "identical_conditions": true,
91 | "identical_contents": true,
92 | "identical_descriptions": true,
93 | "identical_form_names": true,
94 | "if_in_if": true,
95 | "implement_methods": true,
96 | "in_statement_indentation": false,
97 | "indentation": true,
98 | "inline_data_old_versions": true,
99 | "intf_referencing_clas": false,
100 | "keep_single_parameter_on_one_line": true,
101 | "keyword_case": true,
102 | "line_break_multiple_parameters": false,
103 | "line_break_style": false,
104 | "line_length": false,
105 | "line_only_punc": true,
106 | "local_class_naming": false,
107 | "local_testclass_consistency": true,
108 | "local_variable_names": false,
109 | "main_file_contents": true,
110 | "cyclic_oo": true,
111 | "many_parentheses": false,
112 | "unused_ddic": false,
113 | "max_one_method_parameter_per_line": true,
114 | "max_one_statement": true,
115 | "message_exists": true,
116 | "method_implemented_twice": true,
117 | "method_length": false,
118 | "method_overwrites_builtin": true,
119 | "method_parameter_names": false,
120 | "mix_returning": false,
121 | "modify_only_own_db_tables": true,
122 | "msag_consistency": true,
123 | "names_no_dash": true,
124 | "nesting": {
125 | "exclude": [],
126 | "depth": 5
127 | },
128 | "newline_between_methods": false,
129 | "no_public_attributes": false,
130 | "no_yoda_conditions": false,
131 | "object_naming": {
132 | "clas": "^ZC(L|X)\\_",
133 | "intf": "^ZIF\\_",
134 | "prog": "^Z"
135 | },
136 | "obsolete_statement": true,
137 | "omit_parameter_name": false,
138 | "omit_receiving": true,
139 | "parser_702_chaining": true,
140 | "parser_error": true,
141 | "parser_missing_space": true,
142 | "prefer_inline": false,
143 | "prefer_is_not": true,
144 | "prefer_raise_exception_new": true,
145 | "prefer_returning_to_exporting": true,
146 | "prefer_xsdbool": true,
147 | "preferred_compare_operator": true,
148 | "prefix_is_current_class": false,
149 | "reduce_string_templates": true,
150 | "release_idoc": true,
151 | "remove_descriptions": false,
152 | "rfc_error_handling": true,
153 | "select_add_order_by": false,
154 | "select_performance": true,
155 | "selection_screen_naming": true,
156 | "sequential_blank": true,
157 | "short_case": true,
158 | "sicf_consistency": true,
159 | "space_before_colon": true,
160 | "space_before_dot": true,
161 | "sql_escape_host_variables": true,
162 | "start_at_tab": true,
163 | "superclass_final": true,
164 | "sy_modification": true,
165 | "tabl_enhancement_category": true,
166 | "try_without_catch": true,
167 | "type_form_parameters": true,
168 | "types_naming": false,
169 | "uncaught_exception": false,
170 | "unknown_types": true,
171 | "unreachable_code": true,
172 | "unsecure_fae": true,
173 | "unused_methods": true,
174 | "unused_types": {
175 | "exclude": []
176 | },
177 | "unused_variables": {
178 | "exclude": []
179 | },
180 | "use_bool_expression": true,
181 | "use_class_based_exceptions": true,
182 | "use_line_exists": true,
183 | "use_new": true,
184 | "when_others_last": true,
185 | "whitespace_end": true,
186 | "xml_consistency": true
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/src/zcl_result.clas.abap:
--------------------------------------------------------------------------------
1 | CLASS zcl_result DEFINITION
2 | PUBLIC
3 | FINAL
4 | CREATE PRIVATE.
5 |
6 | PUBLIC SECTION.
7 | TYPES: results_type TYPE TABLE OF REF TO zcl_result.
8 | TYPES: BEGIN OF metadata_entry_type,
9 | key TYPE char30,
10 | value TYPE REF TO data,
11 | END OF metadata_entry_type.
12 | TYPES metadata_type TYPE STANDARD TABLE OF metadata_entry_type WITH NON-UNIQUE KEY key.
13 | TYPES error_messages_type TYPE STANDARD TABLE OF string WITH DEFAULT KEY.
14 |
15 | CLASS-METHODS ok IMPORTING value TYPE any OPTIONAL
16 | RETURNING VALUE(result) TYPE REF TO zcl_result.
17 | CLASS-METHODS fail IMPORTING error_message TYPE string OPTIONAL
18 | RETURNING VALUE(result) TYPE REF TO zcl_result.
19 | CLASS-METHODS fail_if
20 | IMPORTING this_is_true TYPE abap_bool
21 | error_message TYPE string OPTIONAL
22 | RETURNING VALUE(result) TYPE REF TO zcl_result.
23 | CLASS-METHODS ok_if
24 | IMPORTING this_is_true TYPE abap_bool
25 | error_message TYPE string OPTIONAL
26 | RETURNING VALUE(result) TYPE REF TO zcl_result.
27 | METHODS is_failure RETURNING VALUE(is_failure) TYPE abap_bool.
28 | METHODS is_ok RETURNING VALUE(is_ok) TYPE abap_bool.
29 | METHODS get_value EXPORTING value TYPE any
30 | RETURNING VALUE(value_reference) TYPE REF TO data
31 | RAISING zcx_result_is_not_ok.
32 | METHODS get_error_message RETURNING VALUE(error_message) TYPE string
33 | RAISING zcx_result_is_not_failure.
34 | METHODS combine_with IMPORTING other_result TYPE REF TO zcl_result
35 | RETURNING VALUE(combined_result) TYPE REF TO zcl_result.
36 | METHODS combine_with_these IMPORTING results TYPE results_type
37 | RETURNING VALUE(combined_result) TYPE REF TO zcl_result.
38 | METHODS with_metadata IMPORTING key TYPE char30
39 | value TYPE any
40 | RETURNING VALUE(result) TYPE REF TO zcl_result.
41 | METHODS get_all_metadata RETURNING VALUE(metadata) TYPE zcl_result=>metadata_type.
42 | METHODS get_metadata IMPORTING key TYPE char30
43 | RETURNING VALUE(value) TYPE REF TO data.
44 | METHODS get_error_messages RETURNING VALUE(error_messages) TYPE zcl_result=>error_messages_type
45 | RAISING zcx_result_is_not_failure.
46 | METHODS has_multiple_error_messages
47 | RETURNING VALUE(has_multiple_error_messages) TYPE abap_bool
48 | RAISING zcx_result_is_not_failure.
49 | METHODS with_error_message
50 | IMPORTING error_message TYPE string
51 | RETURNING VALUE(result) TYPE REF TO zcl_result.
52 |
53 | PRIVATE SECTION.
54 | DATA error_messages TYPE error_messages_type.
55 | DATA value TYPE REF TO data.
56 | DATA result_is_ok TYPE abap_bool.
57 | DATA result_is_failure TYPE abap_bool.
58 | DATA metadata TYPE metadata_type.
59 | METHODS: constructor IMPORTING is_ok TYPE abap_bool
60 | value TYPE any
61 | error_message TYPE string.
62 | METHODS both_results_are_okay IMPORTING result TYPE REF TO zcl_result
63 | RETURNING VALUE(r_result) TYPE abap_bool.
64 | METHODS there_is_nothing_to_combine IMPORTING results TYPE results_type
65 | RETURNING VALUE(result) TYPE abap_bool.
66 | METHODS it_is_the_first_combination RETURNING VALUE(result) TYPE abap_bool.
67 | ENDCLASS.
68 |
69 |
70 |
71 | CLASS zcl_result IMPLEMENTATION.
72 |
73 | METHOD ok.
74 | result = NEW zcl_result( is_ok = abap_true
75 | value = value
76 | error_message = space ).
77 | ENDMETHOD.
78 |
79 | METHOD constructor.
80 | result_is_ok = is_ok.
81 | result_is_failure = xsdbool( is_ok <> abap_true ).
82 | me->value = REF #( value ).
83 | CHECK result_is_failure = abap_true.
84 | CHECK error_message IS NOT INITIAL.
85 | APPEND error_message TO error_messages.
86 | ENDMETHOD.
87 |
88 |
89 | METHOD fail.
90 | result = NEW zcl_result( is_ok = abap_false
91 | value = space
92 | error_message = error_message ).
93 | ENDMETHOD.
94 |
95 |
96 | METHOD combine_with.
97 | IF both_results_are_okay( other_result ).
98 | combined_result = zcl_result=>ok( ).
99 | EXIT.
100 | ENDIF.
101 |
102 | IF is_failure( ) AND other_result->is_failure( ).
103 | APPEND other_result->get_error_message( ) TO error_messages.
104 | combined_result = me.
105 | EXIT.
106 | ENDIF.
107 |
108 | IF is_failure( ).
109 | combined_result = me.
110 | EXIT.
111 | ENDIF.
112 | combined_result = other_result.
113 | ENDMETHOD.
114 |
115 | METHOD both_results_are_okay.
116 | r_result = xsdbool( is_ok( ) AND result->is_ok( ) ).
117 | ENDMETHOD.
118 |
119 |
120 | METHOD combine_with_these.
121 |
122 | IF there_is_nothing_to_combine( results ).
123 | combined_result = me.
124 | EXIT.
125 | ENDIF.
126 | LOOP AT results ASSIGNING FIELD-SYMBOL().
127 | IF it_is_the_first_combination( ).
128 | combined_result = combine_with( ).
129 | ELSE.
130 | combined_result = combined_result->combine_with( ).
131 | ENDIF.
132 | ENDLOOP.
133 | ENDMETHOD.
134 |
135 | METHOD it_is_the_first_combination.
136 | result = xsdbool( sy-tabix = 1 ).
137 | ENDMETHOD.
138 |
139 | METHOD there_is_nothing_to_combine.
140 | result = xsdbool( lines( results ) = 0 ).
141 | ENDMETHOD.
142 |
143 | METHOD is_failure.
144 | is_failure = result_is_failure.
145 | ENDMETHOD.
146 |
147 | METHOD is_ok.
148 | is_ok = result_is_ok.
149 | ENDMETHOD.
150 |
151 | METHOD get_value.
152 | FIELD-SYMBOLS: TYPE any.
153 |
154 | IF is_failure( ).
155 | RAISE EXCEPTION TYPE zcx_result_is_not_ok.
156 | ENDIF.
157 |
158 | IF me->value IS NOT BOUND.
159 | value_reference = REF #( space ).
160 | value = space.
161 | EXIT.
162 | ENDIF.
163 | IF value IS SUPPLIED.
164 | ASSIGN me->value->* TO .
165 | value = .
166 | ENDIF.
167 | value_reference = me->value.
168 | ENDMETHOD.
169 |
170 | METHOD get_error_message.
171 | IF is_ok( ).
172 | RAISE EXCEPTION TYPE zcx_result_is_not_failure.
173 | ENDIF.
174 | CHECK error_messages IS NOT INITIAL.
175 | error_message = error_messages[ 1 ].
176 | ENDMETHOD.
177 |
178 | METHOD fail_if.
179 | IF this_is_true = abap_true.
180 | result = zcl_result=>fail( error_message ).
181 | ELSE.
182 | result = zcl_result=>ok( ).
183 | ENDIF.
184 | ENDMETHOD.
185 |
186 | METHOD ok_if.
187 | result = zcl_result=>fail_if( this_is_true = xsdbool( this_is_true <> abap_true ) error_message = error_message ).
188 | ENDMETHOD.
189 |
190 | METHOD with_metadata.
191 | result = me.
192 | CHECK NOT line_exists( metadata[ key = key ] ).
193 | metadata = VALUE #( BASE metadata ( key = key value = REF #( value ) ) ).
194 | ENDMETHOD.
195 |
196 | METHOD get_all_metadata.
197 | metadata = me->metadata.
198 | ENDMETHOD.
199 |
200 |
201 | METHOD get_metadata.
202 | value = VALUE #( metadata[ key = key ]-value OPTIONAL ).
203 | ENDMETHOD.
204 |
205 |
206 | METHOD get_error_messages.
207 | IF is_ok( ).
208 | RAISE EXCEPTION TYPE zcx_result_is_not_failure.
209 | ENDIF.
210 | error_messages = me->error_messages.
211 | ENDMETHOD.
212 |
213 |
214 | METHOD has_multiple_error_messages.
215 | IF is_ok( ).
216 | RAISE EXCEPTION TYPE zcx_result_is_not_failure.
217 | ENDIF.
218 | has_multiple_error_messages = xsdbool( lines( error_messages ) > 0 ).
219 | ENDMETHOD.
220 |
221 |
222 | METHOD with_error_message.
223 | IF is_ok( ).
224 | result = me.
225 | EXIT.
226 | ENDIF.
227 |
228 | IF error_message IS NOT INITIAL.
229 | APPEND error_message TO error_messages.
230 | ENDIF.
231 | result = me.
232 | ENDMETHOD.
233 |
234 | ENDCLASS.
235 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/dominikpanzer/RESULT-for-ABAP/actions/workflows/lint.yml)
2 | [](https://github.com/dominikpanzer/RESULT-for-ABAP/actions/workflows/unittests.yml)
3 | [](https://twitter.com/PanzerDominik)
4 |
5 | # RESULT for ABAP
6 |
7 | Hi! "RESULT for ABAP" is - surprise, surprise - an ABAP implementation of the Result-Pattern. It's a way to solve a common problem: a method-call can be successful (OK) or it can fail (FAILURE) and the caller needs to know. The Result-Pattern indicates if the operation was successful or failed without the usage of exceptions. It is a quite common pattern in functional languages.
8 |
9 | ## Give a Star! :star:
10 | If you like or are using this project please give it a star. Thanks!
11 |
12 | ## Why should I use RESULT for ABAP instead of exceptions?
13 | * Exceptions are actually only for... well, exceptional cases, like DB errors, locks etc. not for "domain errors" like validations etc. When you expect something (like the user entering wrong/invalid data) than it is actually no case for an exception. You actually seem to expect that users quite often are creative when entering data.
14 | * Exception are often being used as a fancy form of the [GOTO-statement](https://en.wikipedia.org/wiki/Considered_harmful). You often don't know where they will be caught. If they get caught at all. This has been considered harmful in 1968.
15 | * If a method can throw an exception and the calling code does not directly catch it - was this a mistake or on purpose? Will it be catch somewhere up the callstack? You never know, so have fun analyzing the whole stack.
16 | * Exceptions lead to hard to read code, because regular business logic is mixed with technical error handling via TRY...CATCH-blocks, for example when many different exceptions have to be caught.
17 | * Exceptions sometimes are not really helpful, because people tend to wrap all code into a TRY...CATCH-block for CX_ROOT. This can lead to inconsistent data, because a part of the data might already have been processed and persisted. It would be actually better to shortdump.
18 | * Exceptions tend to return only one error, but what if you have multiple errors?
19 | * Often command-methods just return a value like "it worked," which is either ABAP_TRUE or ABAP_FALSE. But no additional error values are available which could be shown in the frontend.
20 | * Often query-methods just return the result of a query and if the result is empty, then this represents an error. But what was the reason for the error?
21 | * Other methods export two values: the actual value and an optional error message. But now you can only use EXPORTING and not RETURNING, because there are two parameters. This leads to hard to read code. Ideally a method should only return one value.
22 | * You could use a structure (value, error_message) to solve that problem. RESULT for ABAP is a comfortable object oriented way of doing this - a standardized solution. Your method simply returns a RESULT. 🦖
23 | * RESULT enables a fluent coding style compared to TRY...CATCH...ENDTRY all over the place.
24 | * Consistently using RESULT as the name for the returning parameter of methods simplifies method definitions and significantly improves readability of your code
25 |
26 | ## Okay, show me an example
27 | ### Creating successful RESULTs
28 | ```abap
29 | * create a RESULT which represents a success (OK)
30 | result = zcl_result=>ok( ).
31 | * another one with additional information ("value", i.e. the key of an object you created or the object itself
32 | result = zcl_result=>ok( 100040340 ).
33 | * read the information again. You need to know the type of the value. that's the downside of a RESULT
34 | * get_value can give you a reference, which works smoothly on new systems
35 | DATA new_partner TYPE bu_partner.
36 | new_partner = result->get_value( )->*.
37 | * or it can give you direct access via the EXPORTING-Parameter, which is comfortable on 7.5x-systems
38 | result->get_value( importing value = new_partner ).
39 | * a result can be generated for example based on a validator-value. FAIL_IF will create a FAILURE,
40 | * whenever the validator returns TRUE. when the validator returns FALSE, its an OK-RESULT
41 | result = zcl_result=>fail_if( validator_returns_false( ) ).
42 | * when a validator returns false and you can add an error message, it will be used if the RESULT is a FAILURE,
43 | * otherwise its an OK-RESULT, like in this example
44 | result = zcl_result=>fail_if( this_is_true = validator_returns_false( ) error_message = 'a wild error occurred' ).
45 | * when a validator returns true, it will be an OK-RESULT
46 | result = zcl_result=>ok_if( validator_returns_true( ) ).
47 | * when a validator returns true its OK. but if the validator would fails, it is a FAILURE with an error message
48 | result = zcl_result=>ok_if( this_is_true = validator_returns_true( ) error_message = 'a wild error occurred' ).
49 | ```
50 | ### Creating failures
51 | ```abap
52 | * create a RESULT which indicates a FAILURE
53 | result = zcl_result=>fail( ).
54 | * with an error message
55 | result = zcl_result=>fail('a wild error occurred').
56 | * when a validator returns true
57 | result = zcl_result=>fail_if( validator_returns_true( ) ).
58 | * when a validator returns true + error message
59 | result = zcl_result=>fail_if( this_is_true = validator_returns_true( ) error_message = 'a wild error occurred' ).
60 | * when a validator returns false
61 | result = zcl_result=>ok_if( validator_returns_false( ) ).
62 | * when a validator returns false
63 | result = zcl_result=>ok_if( this_is_true = validator_returns_false( ) error_message = 'a wild error occurred' ).
64 | * reading an error message / the first error or all errors or all error messages
65 | IF result->has_multiple_error_messages( ).
66 | DATA(error_message) = result->get_error_message( ).
67 | ELSE.
68 | DATA(error_messages) = result->get_error_messages( ).
69 | ENDIF.
70 | * adding additional error messages to a FAILURE
71 | zcl_result=>fail('a wild error occurred')->with_error_message( error_message ).
72 | ```
73 | ### Combining results
74 | Usually there are many validations at the start of a method, so you might like to combine their single RESULTs into a final big one. The typical use case here is "validate X variables and all have to be OK, otherwise it's a FAILURE so stop processing the data". So if there is at least one FAILURE, the RESULT will be a FAILURE. Otherwise the RESULT will be OK. Currently only one error message will be stored. Combined OK-RESULTs don't have a value. You can also return a table of RESULTs from you method if you need the details.
75 | ```abap
76 | * combined RESULT is OK
77 | DATA(result_one) = zcl_result=>ok( ).
78 | DATA(result_two) = zcl_result=>ok( ).
79 | result = result_one->combine_with( result_two ).
80 |
81 | * combined RESULT is a FAILURE
82 | DATA results TYPE zcl_result=>ty_results.
83 | DATA(result_one) = zcl_result=>ok( ).
84 | DATA(result_two) = zcl_result=>fail( error_message ).
85 | DATA(result_three) = zcl_result=>fail( error_message ).
86 | results = VALUE #( ( result_two ) ( result_three ) ).
87 | result = result_one->combine_with_these( results ).
88 | ```
89 |
90 | ### Adding Metadata to a RESULT
91 | If you need more then just the one VALUE of an OK-RESULT, you can add metadata to the result. Metadata are key-value-pairs, with a unique CHAR30 key and the value being a data reference. Metadata can be added to any type of RESULT.
92 | ```abap
93 | * Adding Metadata
94 | DATA(structure) = VALUE zst_metadata_entry( key = 'a' value = REF #( 'random structure' ) ).
95 | result = zcl_result=>ok( )->with_metadata( key = 'a structure' value = structure ).
96 | result->with_metdata( key = 'band' value = 'Slayer' ).
97 |
98 | * Reading metadata with a key
99 | DATA(value) = result->get_metadata( 'a structure' ).
100 | * Reading the whole key-value-table
101 | DATA(metadata) = result->get_all_metadata( ).
102 | ```
103 |
104 | ### Usage of a RESULT in a method
105 | Use the RESULT as a RETURNING parameter:
106 | ```abap
107 | METHOD do_something IMPORTING partner TYPE bu_partner
108 | RETURNING VALUE(result) TYPE REF TO zcl_result.
109 |
110 | ...
111 |
112 | METHOD do_something.
113 | * 100s of lines of arcane logic
114 |
115 | * hooray, no problems at all
116 | result = zcl_result=>ok( 100040340 ).
117 | ENDMETHOD.
118 | ```
119 | ### Processing a RESULT
120 | Use the RESULT-object for flow control as you like:
121 | ```abap
122 | * call a method which returns a result
123 | DATA new_partner TYPE bu_partner.
124 | DATA(result) = do_something( partner ).
125 |
126 | * guarding
127 | IF result->is_failure( ).
128 | DATA(error_message) = result->get_error_message( ).
129 | * log / error for webservice
130 | EXIT.
131 | ENDIF.
132 |
133 | new_partner = result->get_value( )->*.
134 | * do something with partner, i.e. persistence
135 | ```
136 |
137 | ## How to install RESULT for ABAP
138 | You can copy and paste the source code into your system or simply clone this repository with [abapGit](https://abapgit.org/).
139 |
140 | ## Test List
141 | I like to create a simple [acceptance test list](https://agiledojo.de/2018-12-16-tdd-testlist/) before I start coding. It's my personal todo-list. Often the list is very domain-centric, this one is quite technical.
142 |
143 | |Test List|
144 | |----|
145 | :white_check_mark: first release somehow seems to works
146 | :white_check_mark: when `FAIL_IF` gets called with an optional error message "a wild error occurred", the error message gets stored when the RESULT is a failure
147 | :white_check_mark: when `FAIL_IF` has been called with an optional error message "a wild error occurred", `GET_ERROR_MESSAGE` will return "a wild error occurred" when the RESULT is a failure
148 | :white_check_mark: when `FAIL_IF` has been called with an optional error message "a wild error occurred", `GET_ERROR_MESSAGE` will return an exception, when the RESULT is OK
149 | :white_check_mark: when `OK_IF` gets called with an optional error message "a wild error occurred", the error message gets stored when the RESULT is a failure
150 | :white_check_mark: when `OK_IF` has been called with an optional error message "a wild error occurred", `GET_ERROR_MESSAGE` will return "a wild error occurred" when the RESULT is a failure
151 | :white_check_mark: when `OK_IF` has been called with an optional error message "a wild error occurred", `GET_ERROR_MESSAGE` will return an exception, when the RESULT is OK
152 | :white_check_mark: when `OK_IF` has been called with an optional error message "a wild error occurred", `GET_VALUE` a initial value when the RESULT is OK
153 | :white_check_mark: when a OK-RESULT uses a table as a VALUE, the VALUE can be retrieved and has the same number of lines as the original internal table
154 | :white_check_mark: update the docs :japanese_ogre:
155 | :white_check_mark: when the method `WITH_METADATA( key = "name" value = "David Hasselhoff" )` gets called once, the Metadata gets stored
156 | :white_check_mark: when the method `GET_ALL_METADATA( )` gets called after `WITH_METADATA( key = "name" value = "David Hasselhoff" )`, it returns a table with one entry `(name, David Hasselhoff)`
157 | :white_check_mark: when the method `GET_METADATA( name )` gets called after `WITH_METADATA( key = "name" value = "David Hasselhoff" )`, it returns a single value "David Hasselhoff"
158 | :white_check_mark: when the method `GET_ALL_METADATA( )` gets called without `WITH_METDATA` being called before, it returns an initial table
159 | :white_check_mark: when the method `GET_METADATA( date )` gets called after `WITH_METADATA( key = "name" value = "David Hasselhoff" )`, it returns an initial value
160 | :white_check_mark: when the method `WITH_METADATA( key = "name" value = "David Hasselhoff" )` is called with the same key twice, no duplicates get stored and it throws
161 | :white_check_mark: when the method `WITH_METADATA` is called with an initial key then that is okay
162 | :white_check_mark: when the method `WITH_METADATA` is called twice with different keys `( key = "name" value = "David Hasselhoff" ) ( key = "name2" value = "David Hasselhoff" )`, both values get stored
163 | :white_check_mark: when the method `WITH_METADATA( key = "name" value = value )` and value a structure, a structure will be returned by get_metadata( name ).
164 | :white_check_mark: update the docs :japanese_ogre:
165 | :white_check_mark: when `COMBINE_WITH` gets called with two failures, both error messages get stored
166 | :white_check_mark: when `COMBINE_WITH_THESE` gets called with tow failures, both error messages get stored
167 | :white_check_mark: when `GET_ERROR_MESSAGES` gets called for an FAILURE with two error messages, it returns two error messages
168 | :white_check_mark: when `GET_ERROR_MESSAGE` gets called on a FAILURE it returns only the first error message
169 | :white_check_mark: `HAS_MULTIPLE_ERROR_MESSAGES` returns false when there is no error_message for a FAILURE
170 | :white_check_mark: `HAS_MULTIPLE_ERROR_MESSAGES` returns false when there is only one error_message for a FAILURE
171 | :white_check_mark: `HAS_MULTIPLE_ERROR_MESSAGES` returns true when there a multiple error_messages for a FAILURE
172 | :white_check_mark: `HAS_MULTIPLE_ERROR_MESSAGES` throws when OK-RESULT
173 | :white_check_mark: `GET_ERROR_MESSAGE` throws when OK-RESULT
174 | :white_check_mark: when `WITH_ERROR_MESSAGE gets called with an empty message on a FAILURE, it just returns the current result
175 | :white_check_mark: when `WITH_ERROR_MESSAGE( 'a wild error occurred' )` gets called on a FAILURE, the message will be added to the list of error messages and can be retrieved with `GET_ERROR_MESSAGES`
176 | :white_check_mark: when `WITH_ERROR_MESSAGE( 'a wild error occurred' )` gets called on a OK-RESULT it does not do anything but return the result
177 | :white_check_mark: update the docs :japanese_ogre:
178 | :white_check_mark: get_value has `RETURNING` and `EXPORTING` parameters to make handling easier on 7.5x-systems
179 | :black_square_button: your awesome idea
180 |
181 | As you can see I'm usually committing after every green test or even more often. I tend to use the ´zero, one, multiple´ or the ´happy path, unhappy path´ patterns to write my tests to drive my logic.
182 |
183 | ## How to support this project
184 |
185 | PRs are welcome! You can also just pick one of the test list entries from above and implement the solution or implement your own idea. Fix a bug. Improve the docs... whatever suits you.
186 |
187 | Greetings,
188 | Dominik
189 |
190 | follow me on [Twitter](https://twitter.com/PanzerDominik) or [Mastodon](https://sw-development-is.social/web/@PanzerDominik)
191 |
192 |
193 |
--------------------------------------------------------------------------------
/src/zcl_result.clas.testclasses.abap:
--------------------------------------------------------------------------------
1 | CLASS result_tests DEFINITION DEFERRED.
2 | CLASS zcl_result DEFINITION LOCAL FRIENDS result_tests.
3 | CLASS result_tests DEFINITION FINAL FOR TESTING
4 | DURATION SHORT
5 | RISK LEVEL HARMLESS.
6 | PUBLIC SECTION.
7 |
8 | PRIVATE SECTION.
9 | DATA error_message TYPE string VALUE 'A wild error occurred!'.
10 | METHODS create_ok_result FOR TESTING.
11 | METHODS create_ok_result_check_failed FOR TESTING.
12 | METHODS create_failed_result FOR TESTING.
13 | METHODS create_failed_result_check_ok FOR TESTING.
14 | METHODS ok_result_with_value_ref FOR TESTING.
15 | METHODS failed_result_with_error FOR TESTING.
16 | METHODS combine_with_one_all_are_ok FOR TESTING.
17 | METHODS combine_multiple_all_are_ok FOR TESTING.
18 | METHODS combine_ok_and_failure FOR TESTING.
19 | METHODS combine_two_failures FOR TESTING.
20 | METHODS cant_access_error_when_ok FOR TESTING.
21 | METHODS combine_multiple_all_failed FOR TESTING.
22 | METHODS combine_multiple_one_failed FOR TESTING.
23 | METHODS cant_access_value_when_failure FOR TESTING.
24 | METHODS combine_multiple_no_entries FOR TESTING.
25 | METHODS fail_if_true FOR TESTING.
26 | METHODS not_failure_if_false FOR TESTING.
27 | METHODS ok_if_true FOR TESTING.
28 | METHODS not_ok_if_false FOR TESTING.
29 | METHODS ok_result_with_object_as_value FOR TESTING.
30 | METHODS ok_result_with_table_as_value FOR TESTING.
31 | METHODS combine_multiple_two_failed FOR TESTING.
32 |
33 | METHODS fail_if_returns_error_message FOR TESTING.
34 | METHODS fail_if_is_ok_throws_value FOR TESTING.
35 | METHODS ok_if_saves_error_message FOR TESTING.
36 | METHODS ok_if_is_failure_throws_error FOR TESTING.
37 | METHODS ok_if_returns_initial_value FOR TESTING.
38 |
39 | METHODS all_metadata_can_be_read FOR TESTING.
40 | METHODS one_metadata_entry_can_be_read FOR TESTING.
41 | METHODS initial_metadata_table FOR TESTING.
42 | METHODS metadata_key_not_found FOR TESTING.
43 | METHODS no_duplicate_metadata_allowed FOR TESTING.
44 | METHODS metadata_with_empty_key FOR TESTING.
45 | METHODS more_than_one_metadata_entry FOR TESTING.
46 | METHODS metadata_can_handle_structures FOR TESTING.
47 | METHODS failures_two_errormsgs_stored FOR TESTING.
48 | METHODS combine_8_ok_and_failues FOR TESTING.
49 | METHODS retrieve_2_error_messages FOR TESTING.
50 | METHODS has_multiple_works_for_2 FOR TESTING.
51 | METHODS has_multiple_works_for_1 FOR TESTING.
52 | METHODS has_multiple_works_for_0 FOR TESTING.
53 | METHODS has_multiple_throws_for_ok FOR TESTING.
54 | METHODS get_error_msg_throws_for_ok FOR TESTING.
55 | METHODS with_error_message_initial FOR TESTING.
56 | METHODS with_error_message_on_failure FOR TESTING.
57 | METHODS with_error_message_on_ok FOR TESTING.
58 | METHODS get_error_msgs_throws_for_ok FOR TESTING.
59 | METHODS ok_result_with_value FOR TESTING RAISING cx_static_check.
60 |
61 | METHODS this_returns_true RETURNING VALUE(result) TYPE abap_bool.
62 | METHODS this_returns_false RETURNING VALUE(result) TYPE abap_bool.
63 |
64 | ENDCLASS.
65 |
66 |
67 | CLASS result_tests IMPLEMENTATION.
68 |
69 | METHOD create_ok_result.
70 | DATA(result) = zcl_result=>ok( ).
71 |
72 | DATA(is_result_ok) = result->is_ok( ).
73 |
74 | cl_abap_unit_assert=>assert_equals( msg = 'Not OK, but it should be ok' exp = abap_true act = is_result_ok ).
75 | ENDMETHOD.
76 |
77 | METHOD create_ok_result_check_failed.
78 | DATA(result) = zcl_result=>ok( ).
79 |
80 | DATA(is_result_ok) = result->is_failure( ).
81 |
82 | cl_abap_unit_assert=>assert_equals( msg = 'Failed, but it should be ok' exp = abap_false act = is_result_ok ).
83 | ENDMETHOD.
84 |
85 | METHOD create_failed_result.
86 | DATA(result) = zcl_result=>fail( ).
87 |
88 | DATA(is_result_a_failure) = result->is_failure( ).
89 |
90 | cl_abap_unit_assert=>assert_equals( msg = 'Not failed, but it should be failed' exp = abap_true act = is_result_a_failure ).
91 | ENDMETHOD.
92 |
93 | METHOD create_failed_result_check_ok.
94 | DATA(result) = zcl_result=>fail( ).
95 |
96 | DATA(is_result_ok) = result->is_ok( ).
97 |
98 | cl_abap_unit_assert=>assert_equals( msg = 'ok, but it should be failed' exp = abap_false act = is_result_ok ).
99 | ENDMETHOD.
100 |
101 | METHOD ok_result_with_value_ref.
102 | * can save a value of a simple data type like char
103 | DATA id_of_created_object TYPE char10 VALUE '0815'.
104 |
105 | DATA(result) = zcl_result=>ok( id_of_created_object ).
106 | DATA(temporary_value) = result->get_value( ).
107 | DATA(value) = CAST char10( temporary_value ).
108 |
109 | cl_abap_unit_assert=>assert_equals( msg = 'Couldnt access value' exp = id_of_created_object act = value->* ).
110 | ENDMETHOD.
111 |
112 | METHOD ok_result_with_value.
113 | * can save a value of a simple data type like char
114 | DATA id_of_created_object TYPE char10 VALUE '0815'.
115 | DATA value TYPE char10.
116 |
117 | DATA(result) = zcl_result=>ok( id_of_created_object ).
118 | result->get_value( IMPORTING value = value ).
119 |
120 | cl_abap_unit_assert=>assert_equals( msg = 'Couldnt access value' exp = id_of_created_object act = value ).
121 | ENDMETHOD.
122 |
123 | METHOD ok_result_with_object_as_value.
124 | * can save a value with a complex data type like object reference
125 | DATA random_object_reference TYPE REF TO zcl_result.
126 | DATA: value TYPE REF TO zcl_result.
127 | FIELD-SYMBOLS TYPE REF TO zcl_result.
128 |
129 | random_object_reference = zcl_result=>ok( ).
130 | DATA(result) = zcl_result=>ok( random_object_reference ).
131 |
132 | result->get_value( IMPORTING value = value ).
133 |
134 | DATA(temporary_value) = result->get_value( ).
135 | ASSIGN temporary_value->* TO .
136 |
137 | cl_abap_unit_assert=>assert_equals( msg = 'Couldnt access value' exp = random_object_reference act = ).
138 | cl_abap_unit_assert=>assert_equals( msg = 'Couldnt access value' exp = random_object_reference act = value ).
139 | ENDMETHOD.
140 |
141 | METHOD failed_result_with_error.
142 | DATA(result) = zcl_result=>fail( error_message ).
143 |
144 | cl_abap_unit_assert=>assert_equals( msg = 'error message not correct' exp = error_message act = result->get_error_message( ) ).
145 | ENDMETHOD.
146 |
147 | METHOD combine_with_one_all_are_ok.
148 | * combining two RESULTs which are OK leads to a OK-RESULT
149 | DATA(result_one) = zcl_result=>ok( ).
150 | DATA(result_two) = zcl_result=>ok( ).
151 |
152 | DATA(final_result) = result_one->combine_with( result_two ).
153 |
154 | cl_abap_unit_assert=>assert_equals( msg = 'Not OK, but it should be ok' exp = abap_true act = final_result->is_ok( ) ).
155 | ENDMETHOD.
156 |
157 | METHOD combine_multiple_all_are_ok.
158 | DATA results TYPE zcl_result=>results_type.
159 |
160 | DATA(result_one) = zcl_result=>ok( ).
161 | DATA(result_two) = zcl_result=>ok( ).
162 | DATA(result_three) = zcl_result=>ok( ).
163 | DATA(result_four) = zcl_result=>ok( ).
164 | results = VALUE #( ( result_two ) ( result_three ) ( result_four ) ).
165 |
166 | DATA(final_result) = result_one->combine_with_these( results ).
167 |
168 | cl_abap_unit_assert=>assert_equals( msg = 'Not OK, but it should be ok' exp = abap_true act = final_result->is_ok( ) ).
169 | ENDMETHOD.
170 |
171 | METHOD combine_ok_and_failure.
172 | * if two RESULTS get combined and one is a FAILURE, the final result should
173 | * also be a FAILURE
174 | DATA(result_one) = zcl_result=>ok( ).
175 | DATA(result_two) = zcl_result=>fail( error_message ).
176 |
177 | DATA(final_result) = result_one->combine_with( result_two ).
178 |
179 | cl_abap_unit_assert=>assert_equals( msg = 'OK but it should be not OK' exp = abap_false act = final_result->is_ok( ) ).
180 | cl_abap_unit_assert=>assert_equals( msg = 'Errormessage not correct' exp = error_message act = final_result->get_error_message( ) ).
181 | ENDMETHOD.
182 |
183 | METHOD combine_two_failures.
184 | DATA(result_one) = zcl_result=>fail( error_message ).
185 | DATA(result_two) = zcl_result=>fail( error_message ).
186 |
187 | DATA(final_result) = result_one->combine_with( result_two ).
188 |
189 | cl_abap_unit_assert=>assert_equals( msg = 'OK but it should be not OK' exp = abap_false act = final_result->is_ok( ) ).
190 | cl_abap_unit_assert=>assert_equals( msg = 'Errormessage not correct' exp = error_message act = final_result->get_error_message( ) ).
191 | ENDMETHOD.
192 |
193 | METHOD cant_access_error_when_ok.
194 | TRY.
195 | DATA(result) = zcl_result=>ok( ).
196 | result->get_error_message( ).
197 | cl_abap_unit_assert=>fail( ).
198 | CATCH zcx_result_is_not_failure INTO DATA(result_is_no_failure).
199 | cl_abap_unit_assert=>assert_bound( result_is_no_failure ).
200 | ENDTRY.
201 | ENDMETHOD.
202 |
203 | METHOD cant_access_value_when_failure.
204 | * FAILUREs don't have any value. needs to be checked with .is_ok( )
205 | * or it throws
206 | TRY.
207 | DATA(result) = zcl_result=>fail( error_message ).
208 | result->get_value( ).
209 | cl_abap_unit_assert=>fail( ).
210 | CATCH zcx_result_is_not_ok INTO DATA(result_is_not_ok).
211 | cl_abap_unit_assert=>assert_bound( result_is_not_ok ).
212 | ENDTRY.
213 | ENDMETHOD.
214 |
215 | METHOD combine_multiple_all_failed.
216 | DATA results TYPE zcl_result=>results_type.
217 |
218 | * arrange
219 | DATA(result_one) = zcl_result=>fail( error_message ).
220 | DATA(result_two) = zcl_result=>fail( 'no' ).
221 | DATA(result_three) = zcl_result=>fail( 'argh' ).
222 | results = VALUE #( ( result_two ) ( result_three ) ).
223 |
224 | * act
225 | DATA(final_result) = result_one->combine_with_these( results ).
226 |
227 | * assert
228 | cl_abap_unit_assert=>assert_equals( msg = 'OK, but it should be not OK' exp = abap_true act = final_result->is_failure( ) ).
229 | ENDMETHOD.
230 |
231 | METHOD combine_multiple_one_failed.
232 | DATA results TYPE zcl_result=>results_type.
233 |
234 | * arrange
235 | DATA(result_one) = zcl_result=>ok( ).
236 | DATA(result_two) = zcl_result=>fail( error_message ).
237 | DATA(result_three) = zcl_result=>ok( ).
238 | results = VALUE #( ( result_two ) ( result_three ) ).
239 |
240 | * act
241 | DATA(final_result) = result_one->combine_with_these( results ).
242 |
243 | * assert
244 | cl_abap_unit_assert=>assert_equals( msg = 'OK, but it should be not OK' exp = abap_true act = final_result->is_failure( ) ).
245 | ENDMETHOD.
246 |
247 | METHOD combine_multiple_no_entries.
248 | * the results-table is empty, shouldnt change from OK to Failure.
249 | DATA empty_table TYPE zcl_result=>results_type.
250 |
251 | DATA(result_one) = zcl_result=>ok( ).
252 |
253 | DATA(final_result) = result_one->combine_with_these( empty_table ).
254 |
255 | cl_abap_unit_assert=>assert_equals( msg = 'FAILURE, but should be ok' exp = abap_false act = final_result->is_failure( ) ).
256 | ENDMETHOD.
257 |
258 | METHOD fail_if_true.
259 | DATA(result) = zcl_result=>fail_if( this_returns_true( ) ).
260 |
261 | cl_abap_unit_assert=>assert_equals( msg = 'OK, but it should be not OK' exp = abap_true act = result->is_failure( ) ).
262 | ENDMETHOD.
263 |
264 | METHOD not_failure_if_false.
265 | * fails only if parameter is true
266 | DATA(result) = zcl_result=>fail_if( this_returns_false( ) ).
267 |
268 | cl_abap_unit_assert=>assert_equals( msg = 'not OK, but it should be OK' exp = abap_true act = result->is_ok( ) ).
269 | ENDMETHOD.
270 |
271 | METHOD ok_if_true.
272 | DATA(result) = zcl_result=>ok_if( this_returns_true( ) ).
273 |
274 | cl_abap_unit_assert=>assert_equals( msg = 'OK, but it should be not OK' exp = abap_true act = result->is_ok( ) ).
275 | ENDMETHOD.
276 |
277 | METHOD not_ok_if_false.
278 | * ok only if parameter is true
279 | DATA(result) = zcl_result=>ok_if( this_returns_false( ) ).
280 |
281 | cl_abap_unit_assert=>assert_equals( msg = 'OK, but it should be not OK' exp = abap_false act = result->is_ok( ) ).
282 | ENDMETHOD.
283 |
284 | METHOD combine_multiple_two_failed.
285 | DATA results TYPE zcl_result=>results_type.
286 |
287 | * arrange
288 | DATA(result_one) = zcl_result=>ok( ).
289 | DATA(result_two) = zcl_result=>fail( error_message ).
290 | DATA(result_three) = zcl_result=>fail( error_message ).
291 |
292 | results = VALUE #( ( result_two ) ( result_three ) ).
293 |
294 | * act
295 | DATA(final_result) = result_one->combine_with_these( results ).
296 |
297 | * assert
298 | cl_abap_unit_assert=>assert_equals( msg = 'OK, but it should be not OK' exp = abap_true act = final_result->is_failure( ) ).
299 | ENDMETHOD.
300 |
301 | METHOD fail_if_returns_error_message.
302 | DATA(result) = zcl_result=>fail_if( this_is_true = this_returns_true( ) error_message = error_message ).
303 |
304 | cl_abap_unit_assert=>assert_equals( msg = 'unable to get error_message' exp = error_message act = result->get_error_message( ) ).
305 | ENDMETHOD.
306 |
307 | METHOD fail_if_is_ok_throws_value.
308 | * OK RESULTs throw when an error message is requested
309 | TRY.
310 | DATA(result) = zcl_result=>fail_if( this_is_true = this_returns_false( ) error_message = error_message ).
311 | result->get_error_message( ).
312 | cl_abap_unit_assert=>fail( ).
313 | CATCH zcx_result_is_not_failure INTO DATA(result_is_no_failure).
314 | cl_abap_unit_assert=>assert_bound( result_is_no_failure ).
315 | ENDTRY.
316 | ENDMETHOD.
317 |
318 | METHOD ok_if_saves_error_message.
319 | DATA(result) = zcl_result=>ok_if( this_is_true = this_returns_false( ) error_message = error_message ).
320 |
321 | cl_abap_unit_assert=>assert_equals( msg = 'didnt store error_message' exp = error_message act = result->get_error_message( ) ).
322 | ENDMETHOD.
323 |
324 | METHOD ok_if_is_failure_throws_error.
325 | * OK RESULTs throw when an error message is requested
326 | TRY.
327 | DATA(result) = zcl_result=>ok_if( this_is_true = this_returns_true( ) error_message = error_message ).
328 | result->get_error_message( ).
329 | cl_abap_unit_assert=>fail( ).
330 | CATCH zcx_result_is_not_failure INTO DATA(result_is_no_failure).
331 | cl_abap_unit_assert=>assert_bound( result_is_no_failure ).
332 | ENDTRY.
333 | ENDMETHOD.
334 |
335 | METHOD ok_if_returns_initial_value.
336 | * OK_IF doesnt support any VALUE, so returns an empty one
337 | DATA temporary_value TYPE REF TO data.
338 | FIELD-SYMBOLS TYPE any.
339 | DATA(result) = zcl_result=>ok_if( this_is_true = this_returns_true( ) ).
340 |
341 | temporary_value = result->get_value( ).
342 |
343 | ASSIGN temporary_value->* TO .
344 | cl_abap_unit_assert=>assert_initial( ).
345 | ENDMETHOD.
346 |
347 | METHOD ok_result_with_table_as_value.
348 | * arrange
349 | DATA random_string TYPE string VALUE '12345'.
350 |
351 | DATA(a_random_table) = VALUE string_table( ( random_string ) ( random_string ) ( random_string ) ).
352 |
353 | * Act
354 | DATA(result) = zcl_result=>ok( a_random_table ).
355 |
356 | * assert
357 | DATA(temporary_value) = result->get_value( ).
358 | DATA(value) = CAST string_table( temporary_value ).
359 | DATA(number_of_entries) = lines( value->* ).
360 | cl_abap_unit_assert=>assert_equals( msg = 'Couldnt access value' exp = 3 act = number_of_entries ).
361 | ENDMETHOD.
362 |
363 | METHOD all_metadata_can_be_read.
364 | DATA(result) = zcl_result=>ok( )->with_metadata( key = 'name' value = 'David Hasselhoff' ).
365 | DATA(metadata) = result->get_all_metadata( ).
366 |
367 | DATA(number_of_entries) = lines( metadata ).
368 | cl_abap_unit_assert=>assert_equals( msg = 'Metdata could not be received' exp = 1 act = number_of_entries ).
369 | ENDMETHOD.
370 |
371 | METHOD one_metadata_entry_can_be_read.
372 | DATA(result) = zcl_result=>ok( )->with_metadata( key = 'name' value = |David Hasselhoff| ).
373 |
374 | DATA(temporary_value) = result->get_metadata( key = 'name' ).
375 |
376 | DATA(value) = CAST string( temporary_value ).
377 | cl_abap_unit_assert=>assert_equals( msg = 'Metdata entry could not be received' exp = |David Hasselhoff| act = value->* ).
378 | ENDMETHOD.
379 |
380 | METHOD initial_metadata_table.
381 | DATA(result) = zcl_result=>ok( ).
382 | DATA(metadata) = result->get_all_metadata( ).
383 |
384 | DATA(number_of_entries) = lines( metadata ).
385 | cl_abap_unit_assert=>assert_equals( msg = 'Metdata should be empty' exp = 0 act = number_of_entries ).
386 | ENDMETHOD.
387 |
388 | METHOD metadata_key_not_found.
389 | * if there is no metadata which has the provided key, the returned value is
390 | * empty
391 | DATA(result) = zcl_result=>ok( )->with_metadata( key = 'name' value = 'David Hasselhoff' ).
392 | DATA(value) = result->get_metadata( key = 'date' ).
393 |
394 | cl_abap_unit_assert=>assert_initial( value ).
395 | ENDMETHOD.
396 |
397 | METHOD no_duplicate_metadata_allowed.
398 | * a metadata key is only allowed once. it has to be unique.
399 | * it just stores it once
400 | DATA(result) = zcl_result=>ok( )->with_metadata( key = 'name' value = 'David Hasselhoff' ).
401 | result->with_metadata( key = 'name' value = 'David Hasselhoff' ).
402 | DATA(metadata) = result->get_all_metadata( ).
403 |
404 | DATA(number_of_entries) = lines( metadata ).
405 | cl_abap_unit_assert=>assert_equals( msg = 'Metdata has too many lines' exp = 1 act = number_of_entries ).
406 | ENDMETHOD.
407 |
408 | METHOD metadata_with_empty_key.
409 | * an empty key is a valid key
410 | DATA(result) = zcl_result=>ok( )->with_metadata( key = '' value = |David Hasselhoff| ).
411 |
412 | DATA(temporary_value) = result->get_metadata( key = '' ).
413 | DATA(value) = CAST string( temporary_value ).
414 |
415 | cl_abap_unit_assert=>assert_equals( msg = 'Metdata entry could not be received' exp = |David Hasselhoff| act = value->* ).
416 | ENDMETHOD.
417 |
418 | METHOD more_than_one_metadata_entry.
419 | DATA(result) = zcl_result=>ok( )->with_metadata( key = 'name' value = 'David Hasselhoff' ).
420 | result->with_metadata( key = 'best song' value = 'Looking for freedom' ).
421 |
422 | DATA(metadata) = result->get_all_metadata( ).
423 |
424 | DATA(number_of_entries) = lines( metadata ).
425 | cl_abap_unit_assert=>assert_equals( msg = 'Should have 2 entries' exp = 2 act = number_of_entries ).
426 | ENDMETHOD.
427 |
428 | METHOD metadata_can_handle_structures.
429 | DATA(structure) = VALUE zcl_result=>metadata_entry_type( key = 'a' value = REF #( 'random structure' ) ).
430 | DATA(result) = zcl_result=>ok( )->with_metadata( key = 'a structure' value = structure ).
431 |
432 | DATA(temporary_value) = result->get_metadata( 'a structure' ).
433 | DATA(value) = CAST zcl_result=>metadata_entry_type( temporary_value ).
434 |
435 | cl_abap_unit_assert=>assert_equals( msg = 'Metadata not stored' exp = structure act = value->* ).
436 | ENDMETHOD.
437 |
438 | METHOD failures_two_errormsgs_stored.
439 | * arrange
440 | DATA(result_one) = zcl_result=>fail( error_message ).
441 | DATA(result_two) = zcl_result=>fail( error_message ).
442 |
443 | * act
444 | DATA(final_result) = result_one->combine_with( result_two ).
445 |
446 | * assert
447 | DATA(number_of_messages) = lines( final_result->error_messages ).
448 | cl_abap_unit_assert=>assert_equals( msg = 'Doesnt have 2 error messages' exp = 2 act = number_of_messages ).
449 | ENDMETHOD.
450 |
451 | METHOD combine_8_ok_and_failues.
452 | DATA results TYPE zcl_result=>results_type.
453 |
454 | * arrange
455 | DATA(result_one) = zcl_result=>ok( ).
456 | DATA(result_two) = zcl_result=>fail( error_message ).
457 | DATA(result_three) = zcl_result=>fail( error_message ).
458 | DATA(result_four) = zcl_result=>ok( ).
459 | DATA(result_five) = zcl_result=>ok( ).
460 | DATA(result_six) = zcl_result=>fail( error_message ).
461 | DATA(result_seven) = zcl_result=>ok( ).
462 | DATA(result_eight) = zcl_result=>fail( error_message ).
463 |
464 | results = VALUE #( ( result_two ) ( result_three ) ( result_four ) ( result_five )
465 | ( result_six ) ( result_seven ) ( result_eight ) ).
466 |
467 | * act
468 | DATA(final_result) = result_one->combine_with_these( results ).
469 |
470 | * assert
471 | DATA(number_of_messages) = lines( final_result->error_messages ).
472 | cl_abap_unit_assert=>assert_equals( msg = 'Doesnt have 4 error messages' exp = 4 act = number_of_messages ).
473 | cl_abap_unit_assert=>assert_equals( msg = 'OK, but it should be not OK' exp = abap_true act = final_result->is_failure( ) ).
474 | ENDMETHOD.
475 |
476 | METHOD retrieve_2_error_messages.
477 | * arrange
478 | DATA(result_one) = zcl_result=>fail( error_message ).
479 | DATA(result_two) = zcl_result=>fail( error_message ).
480 | DATA(final_result) = result_one->combine_with( result_two ).
481 |
482 | * act
483 | DATA(error_messages) = final_result->get_error_messages( ).
484 |
485 | * assert
486 | DATA(number_of_messages) = lines( error_messages ).
487 | cl_abap_unit_assert=>assert_equals( msg = 'Doesnt have 2 error messages' exp = 2 act = number_of_messages ).
488 | ENDMETHOD.
489 |
490 | METHOD has_multiple_works_for_2.
491 | * arrange
492 | DATA(result_one) = zcl_result=>fail( error_message ).
493 | DATA(result_two) = zcl_result=>fail( error_message ).
494 | DATA(final_result) = result_one->combine_with( result_two ).
495 |
496 | * act
497 | DATA(has_multiple_error_messages) = final_result->has_multiple_error_messages( ).
498 |
499 | * assert
500 | cl_abap_unit_assert=>assert_equals( msg = 'Should return true' exp = abap_true act = has_multiple_error_messages ).
501 | ENDMETHOD.
502 |
503 | METHOD has_multiple_works_for_1.
504 | DATA(result) = zcl_result=>fail( error_message ).
505 |
506 | DATA(has_multiple_error_messages) = result->has_multiple_error_messages( ).
507 |
508 | cl_abap_unit_assert=>assert_equals( msg = 'Should return true' exp = abap_true act = has_multiple_error_messages ).
509 | ENDMETHOD.
510 |
511 | METHOD has_multiple_works_for_0.
512 | DATA(result) = zcl_result=>fail( ).
513 |
514 | DATA(has_multiple_error_messages) = result->has_multiple_error_messages( ).
515 |
516 | cl_abap_unit_assert=>assert_equals( msg = 'Should return false' exp = abap_false act = has_multiple_error_messages ).
517 | ENDMETHOD.
518 |
519 | METHOD has_multiple_throws_for_ok.
520 | TRY.
521 | DATA(result) = zcl_result=>ok( ).
522 | result->has_multiple_error_messages( ).
523 | cl_abap_unit_assert=>fail( ).
524 | CATCH zcx_result_is_not_failure INTO DATA(result_is_no_failure).
525 | cl_abap_unit_assert=>assert_bound( result_is_no_failure ).
526 | ENDTRY.
527 | ENDMETHOD.
528 |
529 | METHOD get_error_msg_throws_for_ok.
530 | TRY.
531 | DATA(result) = zcl_result=>ok( ).
532 | result->get_error_message( ).
533 | cl_abap_unit_assert=>fail( ).
534 | CATCH zcx_result_is_not_failure INTO DATA(result_is_no_failure).
535 | cl_abap_unit_assert=>assert_bound( result_is_no_failure ).
536 | ENDTRY.
537 | ENDMETHOD.
538 |
539 | METHOD get_error_msgs_throws_for_ok.
540 | TRY.
541 | DATA(result) = zcl_result=>ok( ).
542 | result->get_error_messages( ).
543 | cl_abap_unit_assert=>fail( ).
544 | CATCH zcx_result_is_not_failure INTO DATA(result_is_no_failure).
545 | cl_abap_unit_assert=>assert_bound( result_is_no_failure ).
546 | ENDTRY.
547 | ENDMETHOD.
548 |
549 | METHOD with_error_message_initial.
550 | * when parameter is empty it does nothing
551 | DATA(result) = zcl_result=>fail( )->with_error_message( VALUE #( ) ).
552 |
553 | DATA(error_message) = result->get_error_message( ).
554 |
555 | cl_abap_unit_assert=>assert_initial( error_message ).
556 | ENDMETHOD.
557 |
558 | METHOD with_error_message_on_failure.
559 | DATA(result) = zcl_result=>fail( )->with_error_message( error_message ).
560 |
561 | DATA(error) = result->get_error_message( ).
562 |
563 | cl_abap_unit_assert=>assert_equals( msg = 'Should be an error' exp = me->error_message act = error ).
564 | ENDMETHOD.
565 |
566 | METHOD with_error_message_on_ok.
567 | DATA(result) = zcl_result=>ok( )->with_error_message( error_message ).
568 |
569 | DATA(number_of_error_messages) = lines( result->error_messages ).
570 |
571 | cl_abap_unit_assert=>assert_equals( msg = 'Should be 0 for every OK-RESULT' exp = 0 act = number_of_error_messages ).
572 | ENDMETHOD.
573 |
574 | METHOD this_returns_true.
575 | result = abap_true.
576 | ENDMETHOD.
577 |
578 | METHOD this_returns_false.
579 | result = abap_false.
580 | ENDMETHOD.
581 |
582 | ENDCLASS.
583 |
--------------------------------------------------------------------------------