Now that we can create contribution objects and convert them to and from JSON, the next step is to manage a list of contributions and make this list persistent in a JSON file.
We will achieve this with a service called se_contribution_list. Besides maintaining a persistent list of contribution objects, this service will have two more responsibilities:
Assign a unique identifier to each contribution object. The identifier will be an integer number starting at 1 and incremented by 1 for each new object.
Create contribution objects from the following 3 values: first_name, last_name and random_number. This is an important feature because we will later build a web interface that enables users to submit their contribution. The users won't input an identifier or the date and time of their contribution. They only submit their first name, last name and random value for an arc.
se_contribution_list will have two public commands:
Command add_contribution creates a contribution object and then adds the object to the list of contributions saved in the JSON file
Command get_list reads all contributions from the JSON file and returns the complete list.
The signature of command add_contribution is defined as follows:
command add_contribution
in first_name type:user_name end
in last_name type:user_name end
in random_number type:random_1_15 end
in error_handler type:system_error_handler default:se_system_utilities.default_system_error_handler end
out result type:contribution voidable:yes end
out error type:system_error voidable:yes end
out_check check: i_result =r void xor i_error =r void end
script
end
endAs you can see, there is again an error_handler input argument and an error output argument because many things can go wrong. For example the input can be invalid or there is a file write error.
Command get_list can also fail and is therefore defined like this:
command get_list
in error_handler type:system_error_handler default:se_system_utilities.default_system_error_handler end
out result type:!indexed_list<contribution> voidable:yes end
out error type:system_error voidable:yes end
out_check check: i_result =r void xor i_error =r void end
script
end
endNote the type for output argument result: !indexed_list<contribution>. Here we use a generic type that denotes an indexed_list containing only contribution objects. The ! introduces the usage of a generic type, and the value between < and > defines the type of object stored in the list.
![]() | Note |
|---|---|
| For Java programmers: Although the syntax for generic types in Obix is similar to the syntax used in Java, generic types in Obix are conceptually different from those in Java. For example, in Obix there is no type erasure at runtime. This is important because type safety is always guaranteed and type parameter information is available at runtime. |
Now we are ready to write se_contribution_list. The full code with comments and a global test script at the end is shown below. To create the service:
Create file se_contribution_list.osc in directory contribution/
Copy-paste the following code, then save the file and run compile_and_build and run_tests
service contribution_list
// The following two attributes define the files used to store data
// file 'contributions.json' contains the contribution objects in JSON format
// file 'next_contribution_id.txt' contains the next identifier for a new contribution object
// (integer starting at 1 and incremented by 1 each time )
attribute_list type:file private:yes kind:variable
attribute contributions_file default:fa_relative_file.create ( "contributions.json"~ ).make_absolute_file end
attribute next_identifier_file default:fa_relative_file.create ( "next_contribution_id.txt"~ ).make_absolute_file end
end
command add_contribution
in first_name type:user_name end
in last_name type:user_name end
in random_number type:random_1_15 end
%system_error_handler_input_argument
%result_xor_system_error_output < contribution >
script
// define the identifier for the new object
// the service command 'se_text_file_IO.get_next_counter_from_text_file' in Obix's standard library
// is a utility that increments a counter stored in a text file
var positive32 identifier
se_text_file_IO.get_next_counter_from_text_file ( &
file = a_next_identifier_file &
error_handler = i_error_handler ) &
( v_identifier = result &
o_error = error )
if o_error #r void then
exit script
end if
// create the 'contribution' object
o_result = fa_contribution.create ( &
identifier = v_identifier &
first_name = i_first_name &
last_name = i_last_name &
random_number = i_random_number &
date_time = se_date_time.current_local_date_time )
// append the JSON representation of the 'contribution' object to file 'a_contributions_file'
o_error = se_text_file_IO.append_string_to_new_or_existing_file ( &
string = o_result.to_JSON & se_string_constants.current_OS_new_line &
file = a_contributions_file &
error_handler = i_error_handler )
end
end
command get_list
%system_error_handler_input_argument
%result_xor_system_error_output < !indexed_list<contribution> >
script
// report an error if 'a_contributions_file' file doesn't exists
if not a_contributions_file.exists then
o_error = fa_system_error.create ( description = """File {{a_contributions_file}} doesn't exist.""" )
i_error_handler.handle_system_error ( o_error )
exit script
end if
// read all lines from file 'a_contributions_file'
var ty_indexed_string_list text_lines
se_text_file_IO.restore_lines_from_text_file ( &
file = a_contributions_file &
error_handler = i_error_handler ) &
( v_text_lines = result &
o_error = error )
if o_error #r void then
exit script
end if
// create a mutable list of 'contribution' objects
const !mutable_indexed_list<contribution> contribution_list = !mutable_indexed_list_factory<contribution>.create
// add all 'contribution' objects stored in the file to the mutable list
repeat for each string line in text_lines
// skip empty lines
if line.is_empty then
next repeat
end if
var contribution contribution
fa_contribution.create_from_JSON ( &
JSON = line &
error_handler = i_error_handler ) &
( v_contribution = result &
o_error = error )
if o_error #r void then
exit script
end if
c_contribution_list.append ( v_contribution )
end
// convert the mutable list into an immutable list and return the immutable list
o_result = c_contribution_list.make_immutable
end
end
test
script
// create temporary files for test purposes
a_contributions_file = se_file.create_temporary_file.result ( delete_file_on_exit = yes )
a_next_identifier_file = se_file.create_temporary_file.result ( delete_file_on_exit = yes )
// first test with valid data
var contribution result
var system_error error
add_contribution ( first_name = fa_user_name.create ( "Joshua"~ ) &
last_name = fa_user_name.create ( "Gafter"~ ) &
random_number = fa_random_1_15.create ( 5~ ) ) &
( v_result = result &
v_error = error )
verify v_error =r void
verify v_result.identifier =v 1
verify v_result.first_name.value =v "Joshua"~
verify v_result.last_name.value =v "Gafter"~
verify v_result.random_number.value =v 5~
// retain the result for later use
const contribution result1 = v_result
// check the file content
var string file_content = se_text_file_IO.restore_string_from_file.result ( file = a_contributions_file )
verify v_file_content =v v_result.to_JSON & se_string_constants.current_OS_new_line
// read data from file into a list
var !indexed_list<contribution> contribution_list = get_list.result
verify v_contribution_list.item_count =v 1
const contribution first_item = v_contribution_list.item_iterator.next
verify c_first_item.identifier =v 1
verify c_first_item.first_name.value =v "Joshua"~
verify c_first_item.last_name.value =v "Gafter"~
verify c_first_item.random_number.value =v 5~
// create a second object
add_contribution ( first_name = fa_user_name.create ( "Albert"~ ) &
last_name = fa_user_name.create ( "Newton"~ ) &
random_number = fa_random_1_15.create ( 4~ ) ) &
( v_result = result &
v_error = error )
verify v_error =r void
verify v_result.identifier =v 2
verify v_result.first_name.value =v "Albert"~
verify v_result.last_name.value =v "Newton"~
verify v_result.random_number.value =v 4~
// check the file content again
v_file_content = se_text_file_IO.restore_string_from_file.result ( file = a_contributions_file )
verify v_file_content =v c_result1.to_JSON & se_string_constants.current_OS_new_line & v_result.to_JSON & se_string_constants.current_OS_new_line
// read data from file into a list
v_contribution_list = get_list.result
verify v_contribution_list.item_count =v 2
end
end
end service