diff --git a/examples/rest/restbucks_CRUD/restbucks.ecf b/examples/rest/restbucks_CRUD/restbucks.ecf
index a9c1ab2f..079ba852 100644
--- a/examples/rest/restbucks_CRUD/restbucks.ecf
+++ b/examples/rest/restbucks_CRUD/restbucks.ecf
@@ -17,6 +17,7 @@
+
diff --git a/examples/rest/restbucks_CRUD/src/conversion/order_json_serialization.e b/examples/rest/restbucks_CRUD/src/conversion/order_json_serialization.e
deleted file mode 100644
index 9e908534..00000000
--- a/examples/rest/restbucks_CRUD/src/conversion/order_json_serialization.e
+++ /dev/null
@@ -1,167 +0,0 @@
-note
- description: "Summary description for {ORDER_JSON_SERIALIZATION}."
- date: "$Date$"
- revision: "$Revision$"
-
-class
- ORDER_JSON_SERIALIZATION
-
-inherit
- JSON_SERIALIZER
-
- JSON_DESERIALIZER
-
-feature -- Conversion
-
- to_json (obj: detachable ANY; ctx: JSON_SERIALIZER_CONTEXT): JSON_VALUE
- -- JSON value representing the JSON serialization of Eiffel value `obj', in the eventual context `ctx'.
- local
- j_order: JSON_OBJECT
- j_item: JSON_OBJECT
- ja: JSON_ARRAY
- do
- has_error := False
- if attached {ORDER} obj as l_order then
- create j_order.make_with_capacity (4)
- Result := j_order
- j_order.put_string (l_order.id, id_key)
- if attached l_order.location as loc then
- j_order.put_string (loc, location_key)
- end
- j_order.put_string (l_order.status, status_key)
- if attached l_order.items as l_items and then not l_items.is_empty then
- create ja.make (l_items.count)
- j_order.put (ja, items_key)
- across
- l_items as ic
- loop
- if attached {ORDER_ITEM} ic.item as l_item then
- create j_item.make_with_capacity (4)
- j_item.put_string (l_item.name, name_key)
- j_item.put_string (l_item.size, size_key)
- j_item.put_integer (l_item.quantity, quantity_key)
- j_item.put_string (l_item.option, option_key)
- ja.extend (j_item)
- end
- end
- end
- else
- create {JSON_NULL} Result
- has_error := True
- end
- end
-
- from_json (a_json: detachable JSON_VALUE; ctx: JSON_DESERIALIZER_CONTEXT; a_type: detachable TYPE [detachable ANY]): detachable ORDER
- -- .
- local
- l_status: detachable STRING_32
- q: NATURAL_8
- is_valid_from_json: BOOLEAN
- l_name, l_size, l_option: detachable READABLE_STRING_32
- do
- has_error := False
- is_valid_from_json := True
- if attached {JSON_OBJECT} a_json as jobj then
- -- Either new order (i.e no id and no status)
- -- or an existing order with `id` and `status` (could be Void, thus use default).
- if attached {JSON_STRING} jobj.item (status_key) as j_status then
- l_status := j_status.unescaped_string_32
- end
- if
- attached {JSON_STRING} jobj.item (id_key) as j_id
- then
- -- Note: the id has to be valid string 8 value!
- create Result.make (j_id.unescaped_string_8, l_status)
- elseif attached {JSON_NUMBER} jobj.item (id_key) as j_id then
- -- Be flexible and accept json number as id.
- create Result.make (j_id.integer_64_item.out, l_status)
- else
- create Result.make_empty
- if l_status /= Void then
- Result.set_status (l_status)
- end
- end
- if attached {JSON_STRING} jobj.item (location_key) as j_location then
- Result.set_location (j_location.unescaped_string_32)
- end
- if attached {JSON_ARRAY} jobj.item (items_key) as j_items then
- across
- j_items as ic
- loop
- if attached {JSON_OBJECT} ic.item as j_item then
- if
- attached {JSON_NUMBER} j_item.item (quantity_key) as j_quantity and then
- j_quantity.integer_64_item < {NATURAL_8}.Max_value
- then
- q := j_quantity.integer_64_item.to_natural_8
- else
- q := 0
- end
- if
- attached {JSON_STRING} j_item.item (name_key) as j_name and then
- attached {JSON_STRING} j_item.item (size_key) as j_size and then
- attached {JSON_STRING} j_item.item (option_key) as j_option
- then
- l_name := j_name.unescaped_string_32
- l_size := j_size.unescaped_string_32
- l_option := j_option.unescaped_string_32
- if is_valid_item_customization (l_name, l_size, l_option, q) then
- Result.add_item (create {ORDER_ITEM}.make (l_name, l_size, l_option, q))
- else
- is_valid_from_json := False
- end
- else
- is_valid_from_json := False
- end
- end
- end
- end
- if not is_valid_from_json or Result.items.is_empty then
- Result := Void
- end
- else
- is_valid_from_json := a_json = Void or else attached {JSON_NULL} a_json
- Result := Void
- end
- has_error := not is_valid_from_json
- end
-
- has_error: BOOLEAN
- -- Error occurred during last `from_json` or `to_json` execution.
-
-feature {NONE} -- Implementation
-
- id_key: STRING = "id"
-
- location_key: STRING = "location"
-
- status_key: STRING = "status"
-
- items_key: STRING = "items"
-
- name_key: STRING = "name"
-
- size_key: STRING = "size"
-
- quantity_key: STRING = "quantity"
-
- option_key: STRING = "option"
-
-feature -- Validation
-
- is_valid_item_customization (name: READABLE_STRING_GENERAL; size: READABLE_STRING_GENERAL; option: READABLE_STRING_GENERAL; quantity: NATURAL_8): BOOLEAN
- local
- ic: ORDER_ITEM_VALIDATION
- do
- create ic
- Result := ic.is_valid_coffee_type (name) and
- ic.is_valid_milk_type (option) and
- ic.is_valid_size_option (size) and
- quantity > 0
- end
-
-note
- copyright: "2011-2017, Javier Velilla and others"
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
-
-end
diff --git a/examples/rest/restbucks_CRUD/src/database/restbucks_api.e b/examples/rest/restbucks_CRUD/src/database/restbucks_api.e
index 376435ff..9f3fa4d8 100644
--- a/examples/rest/restbucks_CRUD/src/database/restbucks_api.e
+++ b/examples/rest/restbucks_CRUD/src/database/restbucks_api.e
@@ -13,10 +13,9 @@ feature {NONE} -- Initialization
make
local
- db: BASIC_JSON_FS_DATABASE
+ db: BASIC_SED_FS_DATABASE
do
create db.make (database_path)
- db.serialization.register (create {ORDER_JSON_SERIALIZATION}, {ORDER})
database := db
end
diff --git a/examples/rest/restbucks_CRUD/src/resource/order_handler.e b/examples/rest/restbucks_CRUD/src/resource/order_handler.e
index e15a8c0c..3c5656f3 100644
--- a/examples/rest/restbucks_CRUD/src/resource/order_handler.e
+++ b/examples/rest/restbucks_CRUD/src/resource/order_handler.e
@@ -332,24 +332,135 @@ feature {NONE} -- Implementation Repository Layer
feature {NONE} -- Conversion
- order_to_json (obj: ORDER): JSON_VALUE
+ order_to_json (obj: ORDER): JSON_OBJECT
+ local
+ j_order: JSON_OBJECT
+ j_item: JSON_OBJECT
+ ja: JSON_ARRAY
do
- Result := order_serialization.to_json (obj)
- end
-
- order_from_json (jv: JSON_VALUE): detachable ORDER
- do
- if attached {ORDER} order_serialization.from_json (jv, {ORDER}) as o then
- Result := o
+ create Result.make_with_capacity (4)
+ Result.put_string (obj.id, id_key)
+ if attached obj.location as loc then
+ Result.put_string (loc, location_key)
+ end
+ Result.put_string (obj.status, status_key)
+ if attached obj.items as l_items and then not l_items.is_empty then
+ create ja.make (l_items.count)
+ Result.put (ja, items_key)
+ across
+ l_items as ic
+ loop
+ if attached {ORDER_ITEM} ic.item as l_item then
+ create j_item.make_with_capacity (4)
+ j_item.put_string (l_item.name, name_key)
+ j_item.put_string (l_item.size, size_key)
+ j_item.put_integer (l_item.quantity, quantity_key)
+ j_item.put_string (l_item.option, option_key)
+ ja.extend (j_item)
+ end
+ end
end
end
- order_serialization: JSON_SERIALIZATION
+ order_from_json (a_json: JSON_VALUE): detachable ORDER
+ local
+ l_status: detachable STRING_32
+ q: NATURAL_8
+ is_valid_from_json: BOOLEAN
+ l_name, l_size, l_option: detachable READABLE_STRING_32
do
- create Result
- Result.register (create {ORDER_JSON_SERIALIZATION}, {ORDER})
+ is_valid_from_json := True
+ if attached {JSON_OBJECT} a_json as jobj then
+ -- Either new order (i.e no id and no status)
+ -- or an existing order with `id` and `status` (could be Void, thus use default).
+ if attached {JSON_STRING} jobj.item (status_key) as j_status then
+ l_status := j_status.unescaped_string_32
+ end
+ if
+ attached {JSON_STRING} jobj.item (id_key) as j_id
+ then
+ -- Note: the id has to be valid string 8 value!
+ create Result.make (j_id.unescaped_string_8, l_status)
+ elseif attached {JSON_NUMBER} jobj.item (id_key) as j_id then
+ -- Be flexible and accept json number as id.
+ create Result.make (j_id.integer_64_item.out, l_status)
+ else
+ create Result.make_empty
+ if l_status /= Void then
+ Result.set_status (l_status)
+ end
+ end
+ if attached {JSON_STRING} jobj.item (location_key) as j_location then
+ Result.set_location (j_location.unescaped_string_32)
+ end
+ if attached {JSON_ARRAY} jobj.item (items_key) as j_items then
+ across
+ j_items as ic
+ loop
+ if attached {JSON_OBJECT} ic.item as j_item then
+ if
+ attached {JSON_NUMBER} j_item.item (quantity_key) as j_quantity and then
+ j_quantity.integer_64_item < {NATURAL_8}.Max_value
+ then
+ q := j_quantity.integer_64_item.to_natural_8
+ else
+ q := 0
+ end
+ if
+ attached {JSON_STRING} j_item.item (name_key) as j_name and then
+ attached {JSON_STRING} j_item.item (size_key) as j_size and then
+ attached {JSON_STRING} j_item.item (option_key) as j_option
+ then
+ l_name := j_name.unescaped_string_32
+ l_size := j_size.unescaped_string_32
+ l_option := j_option.unescaped_string_32
+ if is_valid_item_customization (l_name, l_size, l_option, q) then
+ Result.add_item (create {ORDER_ITEM}.make (l_name, l_size, l_option, q))
+ else
+ is_valid_from_json := False
+ end
+ else
+ is_valid_from_json := False
+ end
+ end
+ end
+ end
+ if not is_valid_from_json or Result.items.is_empty then
+ Result := Void
+ end
+ else
+ is_valid_from_json := a_json = Void or else attached {JSON_NULL} a_json
+ Result := Void
+ end
end
+ is_valid_item_customization (name: READABLE_STRING_GENERAL; size: READABLE_STRING_GENERAL; option: READABLE_STRING_GENERAL; quantity: NATURAL_8): BOOLEAN
+ local
+ ic: ORDER_ITEM_VALIDATION
+ do
+ create ic
+ Result := ic.is_valid_coffee_type (name) and
+ ic.is_valid_milk_type (option) and
+ ic.is_valid_size_option (size) and
+ quantity > 0
+ end
+
+ id_key: STRING = "id"
+
+ location_key: STRING = "location"
+
+ status_key: STRING = "status"
+
+ items_key: STRING = "items"
+
+ name_key: STRING = "name"
+
+ size_key: STRING = "size"
+
+ quantity_key: STRING = "quantity"
+
+ option_key: STRING = "option"
+
note
copyright: "2011-2017, Javier Velilla and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
diff --git a/examples/rest/restbucks_CRUD/support/simple_db/simple_db-safe.ecf b/examples/rest/restbucks_CRUD/support/simple_db/simple_db-safe.ecf
new file mode 100644
index 00000000..1bee0ca7
--- /dev/null
+++ b/examples/rest/restbucks_CRUD/support/simple_db/simple_db-safe.ecf
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+ /.*json.*\.e$
+
+
+
+
+
+
+
diff --git a/examples/rest/restbucks_CRUD/support/simple_db/simple_db.ecf b/examples/rest/restbucks_CRUD/support/simple_db/simple_db.ecf
new file mode 100644
index 00000000..24f0f190
--- /dev/null
+++ b/examples/rest/restbucks_CRUD/support/simple_db/simple_db.ecf
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+ /.*json.*\.e$
+
+
+
+
+
+
+
diff --git a/examples/rest/restbucks_CRUD/src/database/basic_database.e b/examples/rest/restbucks_CRUD/support/simple_db/src/basic_database.e
similarity index 100%
rename from examples/rest/restbucks_CRUD/src/database/basic_database.e
rename to examples/rest/restbucks_CRUD/support/simple_db/src/basic_database.e
diff --git a/examples/rest/restbucks_CRUD/src/database/basic_json_fs_database.e b/examples/rest/restbucks_CRUD/support/simple_db/src/basic_json_fs_database.e
similarity index 100%
rename from examples/rest/restbucks_CRUD/src/database/basic_json_fs_database.e
rename to examples/rest/restbucks_CRUD/support/simple_db/src/basic_json_fs_database.e
diff --git a/examples/rest/restbucks_CRUD/src/database/basic_memory_database.e b/examples/rest/restbucks_CRUD/support/simple_db/src/basic_memory_database.e
similarity index 97%
rename from examples/rest/restbucks_CRUD/src/database/basic_memory_database.e
rename to examples/rest/restbucks_CRUD/support/simple_db/src/basic_memory_database.e
index 1600cbd5..3003b166 100644
--- a/examples/rest/restbucks_CRUD/src/database/basic_memory_database.e
+++ b/examples/rest/restbucks_CRUD/support/simple_db/src/basic_memory_database.e
@@ -1,6 +1,8 @@
note
description: "[
Basic database for simple example based on memory.
+
+ WARNING: for now, not concurrent compliant.
]"
date: "$Date$"
revision: "$Revision$"
diff --git a/examples/rest/restbucks_CRUD/support/simple_db/src/basic_sed_fs_database.e b/examples/rest/restbucks_CRUD/support/simple_db/src/basic_sed_fs_database.e
new file mode 100644
index 00000000..31d8bc6d
--- /dev/null
+++ b/examples/rest/restbucks_CRUD/support/simple_db/src/basic_sed_fs_database.e
@@ -0,0 +1,174 @@
+note
+ description: "[
+ Basic database for simple example using SED files.
+
+ (no concurrency access control, ...)
+ ]"
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ BASIC_SED_FS_DATABASE
+
+inherit
+ BASIC_DATABASE
+
+create
+ make
+
+feature {NONE} -- Initialization
+
+ make (a_location: PATH)
+ local
+ d: DIRECTORY
+ do
+ location := a_location
+ create serialization
+ ensure_directory_exists (a_location)
+ end
+
+feature -- Access
+
+ location: PATH
+
+ serialization: SED_STORABLE_FACILITIES
+
+feature -- Access
+
+ count_of (a_entry_type: TYPE [detachable ANY]): INTEGER
+ local
+ d: DIRECTORY
+ do
+ create d.make_with_path (location.extended (entry_type_name (a_entry_type)))
+ if d.exists then
+ across
+ d.entries as ic
+ loop
+ if attached ic.item.extension as e and then e.is_case_insensitive_equal ("sed") then
+ Result := Result + 1
+ end
+ end
+ end
+ end
+
+ has (a_entry_type: TYPE [detachable ANY]; a_id: READABLE_STRING_GENERAL): BOOLEAN
+ -- Has entry of type `a_entry_type` associated with id `a_id`?
+ local
+ fut: FILE_UTILITIES
+ do
+ Result := fut.file_path_exists (entry_path (a_entry_type, a_id))
+ end
+
+ item (a_entry_type: TYPE [detachable ANY]; a_id: READABLE_STRING_GENERAL): detachable ANY
+ local
+ f: RAW_FILE
+ s: STRING
+ l_reader: SED_MEDIUM_READER_WRITER
+ do
+ create f.make_with_path (entry_path (a_entry_type, a_id))
+ if f.exists then
+ create s.make (f.count)
+ f.open_read
+ create l_reader.make_for_reading (f)
+ Result := serialization.retrieved (l_reader, True)
+ f.close
+ end
+ end
+
+ save (a_entry_type: TYPE [detachable ANY]; a_entry: detachable ANY; cl_entry_id: CELL [detachable READABLE_STRING_GENERAL])
+ local
+ f: RAW_FILE
+ l_id: detachable READABLE_STRING_GENERAL
+ l_writer: SED_MEDIUM_READER_WRITER
+ do
+ l_id := cl_entry_id.item
+ if l_id = Void then
+ l_id := next_identifier (a_entry_type)
+ cl_entry_id.replace (l_id)
+ end
+ create f.make_with_path (entry_path (a_entry_type, l_id))
+ ensure_directory_exists (f.path.parent)
+ f.open_write
+ if a_entry /= Void then
+ create l_writer.make_for_writing (f)
+ serialization.store (a_entry, l_writer)
+ end
+ f.close
+ end
+
+ delete (a_entry_type: TYPE [detachable ANY]; a_id: READABLE_STRING_GENERAL)
+ local
+ f: RAW_FILE
+ do
+ create f.make_with_path (entry_path (a_entry_type, a_id))
+ if f.exists and then f.is_access_writable then
+ f.delete
+ end
+ end
+
+feature {NONE} -- Implementation
+
+ ensure_directory_exists (dn: PATH)
+ local
+ d: DIRECTORY
+ do
+ create d.make_with_path (dn)
+ if not d.exists then
+ d.recursive_create_dir
+ end
+ end
+
+ entry_path (a_entry_type: TYPE [detachable ANY]; a_id: READABLE_STRING_GENERAL): PATH
+ do
+ Result := location.extended (entry_type_name (a_entry_type)).extended (a_id).appended_with_extension ("sed")
+ end
+
+ entry_type_name (a_entry_type: TYPE [detachable ANY]): STRING
+ do
+ Result := a_entry_type.name.as_lower
+ Result.prune_all ('!')
+ end
+
+ last_id_file_path (a_entry_type: TYPE [detachable ANY]): PATH
+ do
+ Result := location.extended (entry_type_name (a_entry_type)).extended ("last-id")
+ end
+
+ next_identifier (a_entry_type: TYPE [detachable ANY]): STRING_8
+ local
+ i: NATURAL_64
+ f: RAW_FILE
+ s: STRING
+ do
+ create f.make_with_path (last_id_file_path (a_entry_type))
+ ensure_directory_exists (f.path.parent)
+ if f.exists then
+ create s.make (f.count)
+ f.open_read
+ f.read_line
+ s := f.last_string
+ f.close
+ if s.is_natural_64 then
+ i := s.to_natural_64
+ end
+ end
+ from
+ i := i + 1
+ Result := i.out
+ until
+ not has (a_entry_type, Result)
+ loop
+ i := i + 1
+ Result := i.out
+ end
+ f.open_write
+ f.put_string (Result)
+ f.close
+ end
+
+invariant
+
+note
+ copyright: "2011-2017, Javier Velilla and others"
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
+end