├── 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 | [![Run abaplint](https://github.com/dominikpanzer/RESULT-for-ABAP/actions/workflows/lint.yml/badge.svg)](https://github.com/dominikpanzer/RESULT-for-ABAP/actions/workflows/lint.yml) 2 | [![Run Unit Tests](https://github.com/dominikpanzer/RESULT-for-ABAP/actions/workflows/unittests.yml/badge.svg)](https://github.com/dominikpanzer/RESULT-for-ABAP/actions/workflows/unittests.yml) 3 | [![Twitter](https://img.shields.io/twitter/follow/PanzerDominik?style=social)](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 | --------------------------------------------------------------------------------