Refactor rename error_500_cms_response to internal_server_error_cms_response
Added bad_request_error_cms_response
Updated code example to use the new internal_server_error_cms_response instead of error_500_cms_response class.
Removed class error_500_cms_response.
Updated cms setup and configuration design:
Removed CMS_SETUP.configuration: CMS_CONFIGURATION
Removed CMS_CONFIGURATION and replaced it by using the configuration library.
Added CMS_DEFAULT_SETUP.configuration: CONFIG_READER
Addec CMS_SETUP.text_item (name): detachable READABLE_STRING_32 ...
in order to access option not publish by the CMS_SETUP interface.
Removed CMS_SERVICE.configuration: CMS_CONFIGURATION since it was not used.
Moved configuration library from eiffel-lang to cms libraries.
Fixed issue related to ini config when parsing from string content,
and also issue related to section included in file via @include.
Updated cms setup and configuration design:
Removed CMS_SETUP.configuration: CMS_CONFIGURATION
Removed CMS_CONFIGURATION and replaced it by using the configuration library.
Added CMS_DEFAULT_SETUP.configuration: CONFIG_READER
Addec CMS_SETUP.text_item (name): detachable READABLE_STRING_32 ...
in order to access option not publish by the CMS_SETUP interface.
Removed CMS_SERVICE.configuration: CMS_CONFIGURATION since it was not used.
Improved the email service and related.
This commit is contained in:
16
library/configuration/config-safe.ecf
Normal file
16
library/configuration/config-safe.ecf
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="config" uuid="A3762109-D650-40A1-B55A-7D236A17903F" library_target="config">
|
||||
<target name="config">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
16
library/configuration/config.ecf
Normal file
16
library/configuration/config.ecf
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="config" uuid="A3762109-D650-40A1-B55A-7D236A17903F" library_target="config">
|
||||
<target name="config">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="false" void_safety="none" syntax="standard">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json.ecf"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
10
library/configuration/license.lic
Normal file
10
library/configuration/license.lic
Normal file
@@ -0,0 +1,10 @@
|
||||
${NOTE_KEYWORD}
|
||||
copyright: "2011-${YEAR}, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
121
library/configuration/src/config_reader.e
Normal file
121
library/configuration/src/config_reader.e
Normal file
@@ -0,0 +1,121 @@
|
||||
note
|
||||
description: "Summary description for {CONFIG_READER}."
|
||||
author: ""
|
||||
date: "$Date: 2014-12-18 12:37:11 -0300 (ju. 18 de dic. de 2014) $"
|
||||
revision: "$Revision: 96383 $"
|
||||
|
||||
deferred class
|
||||
CONFIG_READER
|
||||
|
||||
inherit
|
||||
SHARED_EXECUTION_ENVIRONMENT
|
||||
|
||||
feature -- Status report
|
||||
|
||||
has_item (k: READABLE_STRING_GENERAL): BOOLEAN
|
||||
-- Has item associated with key `k'?
|
||||
deferred
|
||||
end
|
||||
|
||||
has_error: BOOLEAN
|
||||
-- Has error?
|
||||
--| Syntax error, source not found, ...
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Query
|
||||
|
||||
resolved_text_item (k: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
|
||||
-- String item associated with key `k' and expanded to resolved variables ${varname}.
|
||||
do
|
||||
if attached text_item (k) as s then
|
||||
Result := resolved_expression (s)
|
||||
end
|
||||
end
|
||||
|
||||
text_item (k: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
|
||||
-- String item associated with key `k'.
|
||||
deferred
|
||||
end
|
||||
|
||||
integer_item (k: READABLE_STRING_GENERAL): INTEGER
|
||||
-- Integer item associated with key `k'.
|
||||
deferred
|
||||
ensure
|
||||
not has_item (k) implies Result = 0
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
resolved_items: detachable STRING_TABLE [READABLE_STRING_32]
|
||||
-- Resolved items indexed by expression.
|
||||
|
||||
resolved_expression (exp: READABLE_STRING_GENERAL): STRING_32
|
||||
-- Resolved `exp' using `Current' or else environment variables.
|
||||
local
|
||||
i,n,b,e: INTEGER
|
||||
k: detachable READABLE_STRING_GENERAL
|
||||
c: CHARACTER_32
|
||||
l_resolved_items: like resolved_items
|
||||
l_text: detachable READABLE_STRING_GENERAL
|
||||
do
|
||||
from
|
||||
i := 1
|
||||
n := exp.count
|
||||
create Result.make (n)
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
c := exp[i]
|
||||
if i + 1 < n and then c = '$' and then exp[i+1] = '{' then
|
||||
b := i + 2
|
||||
e := exp.index_of ('}', b) - 1
|
||||
if e > 0 then
|
||||
k := exp.substring (b, e)
|
||||
l_text := Void
|
||||
l_resolved_items := resolved_items
|
||||
if
|
||||
l_resolved_items /= Void and then
|
||||
attached l_resolved_items.item (k) as s
|
||||
then
|
||||
-- Already resolved, reuse value.
|
||||
l_text := s
|
||||
elseif attached text_item (k) as s then
|
||||
-- has such item
|
||||
l_text := s
|
||||
elseif attached execution_environment.item (k) as v then
|
||||
-- Or from environment
|
||||
l_text := v
|
||||
else
|
||||
l_text := exp.substring (i, e + 1)
|
||||
end
|
||||
i := e + 1
|
||||
Result.append_string_general (l_text)
|
||||
else
|
||||
Result.extend (c)
|
||||
end
|
||||
else
|
||||
Result.extend (c)
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Duplication
|
||||
|
||||
sub_config (k: READABLE_STRING_GENERAL): detachable CONFIG_READER
|
||||
-- Configuration representing a subset of Current for key `k'.
|
||||
deferred
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
463
library/configuration/src/ini_config.e
Normal file
463
library/configuration/src/ini_config.e
Normal file
@@ -0,0 +1,463 @@
|
||||
note
|
||||
description: "[
|
||||
Object parsing a .ini file.
|
||||
|
||||
Lines starting by '#', or ';', or "--" are comments
|
||||
Section are declared using brackets "[section_name]"
|
||||
Values are declared as "key = value"
|
||||
List values are declared as multiple lines "key[] = value"
|
||||
example:
|
||||
--------------------
|
||||
[first_section]
|
||||
one = abc
|
||||
|
||||
[second_section]
|
||||
two = def
|
||||
lst[] = a
|
||||
lst[] = b
|
||||
lst[] = c
|
||||
|
||||
[third_section]
|
||||
three = ghi
|
||||
table[a] = foo
|
||||
table[b] = bar
|
||||
--------------------
|
||||
|
||||
Values are accessible from `items' or by section from `sections'
|
||||
`item' has smart support for '.'
|
||||
|
||||
item ("one") -> abc
|
||||
item ("two") -> def
|
||||
item ("second_section.two") -> def
|
||||
item ("table[b]") -> foo
|
||||
item ("table.b") -> foo
|
||||
item ("third_section.table[b]") -> foo
|
||||
item ("third_section.table.b") -> foo
|
||||
|
||||
notes:
|
||||
it is considered that the .ini file is utf-8 encoded
|
||||
keys are unicode string
|
||||
values are stored UTF-8 string, but one can use unicode_string_item to convert to STRING_32
|
||||
|
||||
Additional features:
|
||||
- Include other files:
|
||||
@include=file-to-include
|
||||
|
||||
]"
|
||||
date: "$Date: 2014-12-18 12:37:11 -0300 (ju. 18 de dic. de 2014) $"
|
||||
revision: "$Revision: 96383 $"
|
||||
|
||||
class
|
||||
INI_CONFIG
|
||||
|
||||
inherit
|
||||
CONFIG_READER
|
||||
|
||||
TABLE_ITERABLE [ANY, READABLE_STRING_GENERAL]
|
||||
|
||||
SHARED_EXECUTION_ENVIRONMENT
|
||||
|
||||
create
|
||||
make_from_file,
|
||||
make_from_string,
|
||||
make_from_items
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make_from_file (p: PATH)
|
||||
do
|
||||
initialize
|
||||
parse_file (p)
|
||||
end
|
||||
|
||||
make_from_string (a_content: STRING_8)
|
||||
do
|
||||
initialize
|
||||
parse_content (a_content)
|
||||
end
|
||||
|
||||
make_from_items (a_items: like items)
|
||||
do
|
||||
initialize
|
||||
across
|
||||
a_items as ic
|
||||
loop
|
||||
items.force (ic.item, ic.key)
|
||||
end
|
||||
end
|
||||
|
||||
initialize
|
||||
-- Initialize data.
|
||||
do
|
||||
associated_path := Void
|
||||
create utf
|
||||
create items.make (0)
|
||||
create sections.make (0)
|
||||
end
|
||||
|
||||
reset
|
||||
-- Reset internal data.
|
||||
do
|
||||
has_error := False
|
||||
items.wipe_out
|
||||
sections.wipe_out
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
has_item (k: READABLE_STRING_GENERAL): BOOLEAN
|
||||
-- Has item associated with key `k'?
|
||||
do
|
||||
Result := item (k) /= Void
|
||||
end
|
||||
|
||||
has_error: BOOLEAN
|
||||
-- Has error?
|
||||
--| Syntax error, source not found, ...
|
||||
|
||||
feature -- Access: Config Reader
|
||||
|
||||
text_item (k: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
|
||||
-- String item associated with key `k'.
|
||||
local
|
||||
obj: like item
|
||||
do
|
||||
obj := item (k)
|
||||
if attached {READABLE_STRING_32} obj as s32 then
|
||||
Result := s32
|
||||
elseif attached {READABLE_STRING_8} obj as s then
|
||||
Result := utf.utf_8_string_8_to_escaped_string_32 (s)
|
||||
end
|
||||
end
|
||||
|
||||
integer_item (k: READABLE_STRING_GENERAL): INTEGER
|
||||
-- Integer item associated with key `k'.
|
||||
do
|
||||
if attached {READABLE_STRING_GENERAL} item (k) as s then
|
||||
Result := s.to_integer
|
||||
end
|
||||
end
|
||||
|
||||
associated_path: detachable PATH
|
||||
-- If current was built from a filename, return this path.
|
||||
|
||||
feature -- Duplication
|
||||
|
||||
sub_config (k: READABLE_STRING_GENERAL): detachable CONFIG_READER
|
||||
-- Configuration representing a subset of Current for key `k'.
|
||||
do
|
||||
if attached sections.item (k) as l_items then
|
||||
create {INI_CONFIG} Result.make_from_items (l_items)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
item (k: READABLE_STRING_GENERAL): detachable ANY
|
||||
-- Value associated with key `k'.
|
||||
local
|
||||
i: INTEGER
|
||||
s,sk: detachable READABLE_STRING_GENERAL
|
||||
do
|
||||
-- Try first directly in values
|
||||
Result := items.item (k)
|
||||
if Result = Void then
|
||||
--| If there is a dot
|
||||
--| this could be
|
||||
--| section.variable
|
||||
--| variable.name or variable[name]
|
||||
i := k.index_of ('.', 1)
|
||||
if i > 0 then
|
||||
s := k.head (i - 1)
|
||||
-- then search first in section
|
||||
if attached sections.item (s) as l_section then
|
||||
sk := k.substring (i + 1, k.count)
|
||||
Result := l_section.item (sk)
|
||||
if Result = Void then
|
||||
Result := item_from_values (l_section, sk)
|
||||
end
|
||||
end
|
||||
if Result = Void then
|
||||
i := k.index_of ('.', i + 1)
|
||||
if i > 0 then
|
||||
-- There is another dot .. could be due to include
|
||||
across
|
||||
sections as ic
|
||||
until
|
||||
Result /= Void
|
||||
loop
|
||||
s := ic.key
|
||||
-- If has other dot, this may be in sub sections ...
|
||||
if s.has ('.') and then k.starts_with (s) and then k[s.count + 1] = '.' then
|
||||
if attached sections.item (s) as l_section then
|
||||
sk := k.substring (s.count + 2, k.count)
|
||||
Result := l_section.item (sk)
|
||||
if Result = Void then
|
||||
Result := item_from_values (l_section, sk)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if Result = Void then
|
||||
-- otherwise in values object.
|
||||
Result := item_from_values (items, k)
|
||||
end
|
||||
end
|
||||
else
|
||||
--| Could be
|
||||
--| variable[name]
|
||||
Result := item_from_values (items, k)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
items: STRING_TABLE [ANY]
|
||||
|
||||
sections: STRING_TABLE [like items]
|
||||
|
||||
feature -- Access
|
||||
|
||||
new_cursor: TABLE_ITERATION_CURSOR [ANY, READABLE_STRING_GENERAL]
|
||||
-- Fresh cursor associated with current structure
|
||||
do
|
||||
Result := items.new_cursor
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
item_from_values (a_values: STRING_TABLE [ANY]; k: READABLE_STRING_GENERAL): detachable ANY
|
||||
local
|
||||
i,j: INTEGER
|
||||
s: READABLE_STRING_GENERAL
|
||||
do
|
||||
Result := a_values.item (k)
|
||||
if Result = Void then
|
||||
i := k.index_of ('.', 1)
|
||||
if i > 0 then
|
||||
s := k.head (i - 1)
|
||||
if attached {STRING_TABLE [ANY]} a_values.item (s) as l_table then
|
||||
Result := l_table.item (k.substring (i + 1, k.count))
|
||||
end
|
||||
else
|
||||
i := k.index_of ('[', 1)
|
||||
if i > 0 then
|
||||
j := k.index_of (']', i + 1)
|
||||
if j = k.count then
|
||||
s := k.head (i - 1)
|
||||
if attached {STRING_TABLE [ANY]} a_values.item (s) as l_table then
|
||||
Result := l_table.item (k.substring (i + 1, j - 1))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
record_in_section (obj: ANY; k: READABLE_STRING_GENERAL; a_section: READABLE_STRING_GENERAL)
|
||||
local
|
||||
v: detachable like items
|
||||
do
|
||||
v := sections.item (a_section)
|
||||
if v = Void then
|
||||
create v.make (1)
|
||||
sections.force (v, a_section)
|
||||
end
|
||||
v.force (obj, k)
|
||||
end
|
||||
|
||||
parse_content (a_content: STRING_8)
|
||||
local
|
||||
i,j,n: INTEGER
|
||||
s: READABLE_STRING_8
|
||||
do
|
||||
last_section_name := Void
|
||||
from
|
||||
i := 1
|
||||
n := a_content.count
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
j := a_content.index_of ('%N', i)
|
||||
if j > 0 then
|
||||
s := a_content.substring (i, j - 1)
|
||||
i := j + 1
|
||||
if i <= n and then a_content[i] = '%R' then
|
||||
i := i + 1
|
||||
end
|
||||
else
|
||||
j := n
|
||||
s := a_content.substring (i, j)
|
||||
i := j + 1
|
||||
end
|
||||
analyze_line (s, Void)
|
||||
variant
|
||||
i
|
||||
end
|
||||
last_section_name := Void
|
||||
rescue
|
||||
last_section_name := Void
|
||||
has_error := True
|
||||
end
|
||||
|
||||
parse_file (p: PATH)
|
||||
do
|
||||
associated_path := p
|
||||
last_section_name := Void
|
||||
import_path (p, Void)
|
||||
last_section_name := Void
|
||||
end
|
||||
|
||||
import_path (p: PATH; a_section_prefix: detachable READABLE_STRING_32)
|
||||
-- Import config from path `p'.
|
||||
local
|
||||
f: PLAIN_TEXT_FILE
|
||||
l_last_section_name: like last_section_name
|
||||
retried: BOOLEAN
|
||||
do
|
||||
if retried then
|
||||
has_error := True
|
||||
else
|
||||
l_last_section_name := last_section_name
|
||||
last_section_name := Void
|
||||
create f.make_with_path (p)
|
||||
if f.exists and then f.is_access_readable then
|
||||
f.open_read
|
||||
from
|
||||
until
|
||||
f.exhausted or f.end_of_file
|
||||
loop
|
||||
f.read_line_thread_aware
|
||||
analyze_line (f.last_string, a_section_prefix)
|
||||
end
|
||||
f.close
|
||||
else
|
||||
-- File not readable
|
||||
has_error := True
|
||||
end
|
||||
end
|
||||
last_section_name := l_last_section_name
|
||||
rescue
|
||||
retried := True
|
||||
retry
|
||||
end
|
||||
|
||||
analyze_line (a_line: STRING_8; a_section_prefix: detachable READABLE_STRING_32)
|
||||
-- Analyze line `a_line'.
|
||||
local
|
||||
k,sk: STRING_32
|
||||
v: STRING_8
|
||||
obj: detachable ANY
|
||||
lst: LIST [STRING_8]
|
||||
tb: STRING_TABLE [STRING_8]
|
||||
i,j: INTEGER
|
||||
l_section_name: like last_section_name
|
||||
do
|
||||
obj := Void
|
||||
a_line.left_adjust
|
||||
if
|
||||
a_line.is_empty
|
||||
or a_line.is_whitespace
|
||||
or a_line.starts_with_general ("#")
|
||||
or a_line.starts_with_general (";")
|
||||
or a_line.starts_with_general ("--")
|
||||
then
|
||||
-- Ignore
|
||||
elseif a_line.starts_with_general ("[") then
|
||||
i := a_line.index_of (']', 1)
|
||||
l_section_name := utf.utf_8_string_8_to_string_32 (a_line.substring (2, i - 1))
|
||||
l_section_name.left_adjust
|
||||
l_section_name.right_adjust
|
||||
if a_section_prefix /= Void then
|
||||
l_section_name.prepend_character ('.')
|
||||
l_section_name.prepend (a_section_prefix)
|
||||
end
|
||||
last_section_name := l_section_name
|
||||
else
|
||||
i := a_line.index_of ('=', 1)
|
||||
if i > 1 then
|
||||
k := utf.utf_8_string_8_to_string_32 (a_line.head (i - 1))
|
||||
k.right_adjust
|
||||
v := a_line.substring (i + 1, a_line.count)
|
||||
v.left_adjust
|
||||
v.right_adjust
|
||||
|
||||
|
||||
if k.is_case_insensitive_equal_general ("@include") then
|
||||
import_path (create {PATH}.make_from_string (v), last_section_name)
|
||||
else
|
||||
i := k.index_of ('[', 1)
|
||||
if i > 0 then
|
||||
j := k.index_of (']', i + 1)
|
||||
if j = i + 1 then -- ends_with "[]"
|
||||
k.keep_head (i - 1)
|
||||
if attached {LIST [STRING_8]} items.item (k) as l_list then
|
||||
lst := l_list
|
||||
else
|
||||
create {ARRAYED_LIST [STRING_8]} lst.make (1)
|
||||
record_item (lst, k, a_section_prefix)
|
||||
end
|
||||
lst.force (v)
|
||||
obj := lst
|
||||
elseif j > i then
|
||||
-- table
|
||||
sk := k.substring (i + 1, j - 1)
|
||||
sk.left_adjust
|
||||
sk.right_adjust
|
||||
k.keep_head (i - 1)
|
||||
if attached {STRING_TABLE [STRING_8]} items.item (k) as l_table then
|
||||
tb := l_table
|
||||
else
|
||||
create tb.make (1)
|
||||
record_item (tb, k, a_section_prefix)
|
||||
end
|
||||
tb.force (v, sk)
|
||||
obj := tb
|
||||
else
|
||||
-- Error missing closing ']'
|
||||
has_error := True
|
||||
end
|
||||
else
|
||||
record_item (v, k, a_section_prefix)
|
||||
obj := v
|
||||
end
|
||||
|
||||
if attached last_section_name as l_session and then obj /= Void then
|
||||
record_in_section (obj, k, l_session)
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Error
|
||||
has_error := True
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
record_item (v: ANY; k: READABLE_STRING_32; a_section_prefix: detachable READABLE_STRING_32)
|
||||
do
|
||||
if a_section_prefix /= Void then
|
||||
items.force (v, a_section_prefix + {STRING_32} "." + k)
|
||||
else
|
||||
items.force (v, k)
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
last_section_name: detachable STRING_32
|
||||
|
||||
utf: UTF_CONVERTER
|
||||
|
||||
invariant
|
||||
|
||||
note
|
||||
copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
175
library/configuration/src/json_config.e
Normal file
175
library/configuration/src/json_config.e
Normal file
@@ -0,0 +1,175 @@
|
||||
note
|
||||
description: "Object parsing a JSON configuration file."
|
||||
date: "$Date: 2014-12-18 12:37:11 -0300 (ju. 18 de dic. de 2014) $"
|
||||
revision: "$Revision: 96383 $"
|
||||
|
||||
class
|
||||
JSON_CONFIG
|
||||
|
||||
inherit
|
||||
CONFIG_READER
|
||||
|
||||
create
|
||||
make_from_file,
|
||||
make_from_string,
|
||||
make_from_json_object
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make_from_file (p: PATH)
|
||||
-- Create object from a file `p'
|
||||
do
|
||||
parse (p)
|
||||
end
|
||||
|
||||
make_from_string (a_json: READABLE_STRING_8)
|
||||
-- Create current config from string `a_json'.
|
||||
local
|
||||
l_parser: JSON_PARSER
|
||||
do
|
||||
create l_parser.make_with_string (a_json)
|
||||
l_parser.parse_content
|
||||
if
|
||||
l_parser.is_valid and then
|
||||
attached l_parser.parsed_json_object as j_o
|
||||
then
|
||||
make_from_json_object (j_o)
|
||||
else
|
||||
has_error := True
|
||||
end
|
||||
end
|
||||
|
||||
make_from_json_object (a_object: JSON_OBJECT)
|
||||
-- Create current config from JSON `a_object'.
|
||||
do
|
||||
associated_path := Void
|
||||
json_value := a_object
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
has_item (k: READABLE_STRING_GENERAL): BOOLEAN
|
||||
-- Has item associated with key `k'?
|
||||
do
|
||||
Result := item (k) /= Void
|
||||
end
|
||||
|
||||
has_error: BOOLEAN
|
||||
-- Has error?
|
||||
--| Syntax error, source not found, ...
|
||||
|
||||
feature -- Access: Config Reader
|
||||
|
||||
text_item (k: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
|
||||
-- String item associated with query `k'.
|
||||
do
|
||||
if attached {JSON_STRING} item (k) as l_string then
|
||||
Result := l_string.unescaped_string_32
|
||||
elseif attached {JSON_NUMBER} item (k) as l_number then
|
||||
Result := l_number.item
|
||||
end
|
||||
end
|
||||
|
||||
integer_item (k: READABLE_STRING_GENERAL): INTEGER
|
||||
-- Integer item associated with key `k'.
|
||||
do
|
||||
if attached {JSON_NUMBER} item (k) as l_number then
|
||||
Result := l_number.item.to_integer
|
||||
end
|
||||
end
|
||||
|
||||
associated_path: detachable PATH
|
||||
-- If current was built from a filename, return this path.
|
||||
|
||||
feature -- Duplication
|
||||
|
||||
sub_config (k: READABLE_STRING_GENERAL): detachable CONFIG_READER
|
||||
-- Configuration representing a subset of Current for key `k'.
|
||||
do
|
||||
if attached {JSON_OBJECT} item (k) as j_o then
|
||||
create {JSON_CONFIG} Result.make_from_json_object (j_o)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
item (k: READABLE_STRING_GENERAL): detachable JSON_VALUE
|
||||
-- Item associated with query `k' if any.
|
||||
-- `k' can be a single name such as "foo",
|
||||
-- or a qualified name such as "foo.bar" (assuming that "foo" is associated with a JSON object).
|
||||
do
|
||||
if attached json_value as obj then
|
||||
Result := object_json_value (obj, k.to_string_32)
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
object_json_value (a_object: JSON_OBJECT; a_query: READABLE_STRING_32): detachable JSON_VALUE
|
||||
-- Item associated with query `a_query' from object `a_object' if any.
|
||||
local
|
||||
i: INTEGER
|
||||
h: READABLE_STRING_32
|
||||
do
|
||||
i := a_query.index_of ('.', 1)
|
||||
if i > 1 then
|
||||
h := a_query.head (i - 1)
|
||||
if attached {JSON_OBJECT} a_object.item (h) as l_obj then
|
||||
Result := object_json_value (l_obj, a_query.substring (i + 1, a_query.count))
|
||||
else
|
||||
-- This is not an object...
|
||||
Result := Void
|
||||
end
|
||||
else
|
||||
Result := a_object.item (a_query)
|
||||
end
|
||||
end
|
||||
|
||||
parse (a_path: PATH)
|
||||
local
|
||||
l_parser: JSON_PARSER
|
||||
do
|
||||
associated_path := a_path
|
||||
has_error := False
|
||||
if attached json_file_from (a_path) as json_file then
|
||||
l_parser := new_json_parser (json_file)
|
||||
l_parser.parse_content
|
||||
if
|
||||
l_parser.is_valid and then
|
||||
attached l_parser.parsed_json_object as jv
|
||||
then
|
||||
json_value := jv
|
||||
else
|
||||
has_error := True
|
||||
end
|
||||
else
|
||||
has_error := True
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- JSON
|
||||
|
||||
json_value: detachable JSON_OBJECT
|
||||
-- Possible json object representation.
|
||||
|
||||
json_file_from (a_fn: PATH): detachable STRING
|
||||
do
|
||||
Result := (create {JSON_FILE_READER}).read_json_from (a_fn.name.out)
|
||||
end
|
||||
|
||||
new_json_parser (a_string: STRING): JSON_PARSER
|
||||
do
|
||||
create Result.make_with_string (a_string)
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
18
library/configuration/tests/config_tests-safe.ecf
Normal file
18
library/configuration/tests/config_tests-safe.ecf
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="config_tests" uuid="AD1DE0F7-BC8A-4A17-9A44-56C917BD5604">
|
||||
<target name="config_tests">
|
||||
<root class="TEST_CONFIG_READER_SET" feature="default_create"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="transitional" syntax="standard">
|
||||
</option>
|
||||
<setting name="concurrency" value="none"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="config" location="..\config-safe.ecf" readonly="false"/>
|
||||
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
|
||||
<tests name="src" location=".\"/>
|
||||
</target>
|
||||
</system>
|
||||
18
library/configuration/tests/config_tests.ecf
Normal file
18
library/configuration/tests/config_tests.ecf
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="config_tests" uuid="AD1DE0F7-BC8A-4A17-9A44-56C917BD5604">
|
||||
<target name="config_tests">
|
||||
<root class="TEST_CONFIG_READER_SET" feature="default_create"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" void_safety="none" syntax="standard">
|
||||
</option>
|
||||
<setting name="concurrency" value="none"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="config" location="..\config.ecf" readonly="false"/>
|
||||
<library name="testing" location="$ISE_LIBRARY\library\testing\testing.ecf"/>
|
||||
<tests name="src" location=".\"/>
|
||||
</target>
|
||||
</system>
|
||||
177
library/configuration/tests/test_config_reader_set.e
Normal file
177
library/configuration/tests/test_config_reader_set.e
Normal file
@@ -0,0 +1,177 @@
|
||||
note
|
||||
description: "[
|
||||
Testing suite for CONFIG_READER .
|
||||
]"
|
||||
author: "$Author: jfiat $"
|
||||
date: "$Date: 2014-12-18 12:37:11 -0300 (ju. 18 de dic. de 2014) $"
|
||||
revision: "$Revision: 96383 $"
|
||||
|
||||
class
|
||||
TEST_CONFIG_READER_SET
|
||||
|
||||
inherit
|
||||
EQA_TEST_SET
|
||||
|
||||
feature -- Test
|
||||
|
||||
test_ini
|
||||
local
|
||||
cfg: CONFIG_READER
|
||||
do
|
||||
create {INI_CONFIG} cfg.make_from_string ("[
|
||||
foo = bar
|
||||
|
||||
[first]
|
||||
abc = 1
|
||||
def = and so on
|
||||
ghi = "path"
|
||||
|
||||
[ second ]
|
||||
this = 1
|
||||
is = 2
|
||||
the = 3
|
||||
end = 4
|
||||
|
||||
]")
|
||||
|
||||
assert ("is valid", not cfg.has_error)
|
||||
assert ("has_item (foo)", cfg.has_item ("foo"))
|
||||
assert ("has_item (abc)", cfg.has_item ("abc"))
|
||||
assert ("has_item (def)", cfg.has_item ("def"))
|
||||
assert ("has_item (ghi)", cfg.has_item ("ghi"))
|
||||
assert ("has_item (this)", cfg.has_item ("this"))
|
||||
assert ("has_item (is)", cfg.has_item ("is"))
|
||||
assert ("has_item (the)", cfg.has_item ("the"))
|
||||
assert ("has_item (end)", cfg.has_item ("end"))
|
||||
|
||||
assert ("item (foo)", attached cfg.text_item ("foo") as v and then v.same_string_general ("bar"))
|
||||
assert ("item (abc)", attached cfg.text_item ("abc") as v and then v.same_string_general ("1"))
|
||||
assert ("item (def)", attached cfg.text_item ("def") as v and then v.same_string_general ("and so on"))
|
||||
assert ("item (ghi)", attached cfg.text_item ("ghi") as v and then v.same_string_general ("%"path%""))
|
||||
assert ("item (this)", attached cfg.text_item ("this") as v and then v.same_string_general ("1"))
|
||||
assert ("item (is)", attached cfg.text_item ("is") as v and then v.same_string_general ("2"))
|
||||
assert ("item (the)", attached cfg.text_item ("the") as v and then v.same_string_general ("3"))
|
||||
assert ("item (end)", attached cfg.text_item ("end") as v and then v.same_string_general ("4"))
|
||||
|
||||
assert ("has_item (first.abc)", cfg.has_item ("first.abc"))
|
||||
assert ("item (first.abc)", attached cfg.text_item ("first.abc") as v and then v.same_string_general ("1"))
|
||||
assert ("has_item (second.is)", cfg.has_item ("second.is"))
|
||||
assert ("item (second.is)", attached cfg.text_item ("second.is") as v and then v.same_string_general ("2"))
|
||||
|
||||
if attached cfg.sub_config ("second") as cfg_second then
|
||||
assert ("has_item (is)", cfg_second.has_item ("is"))
|
||||
assert ("item (is)", attached cfg_second.text_item ("is") as v and then v.same_string_general ("2"))
|
||||
|
||||
else
|
||||
assert ("has second", False)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
test_resolver_ini
|
||||
local
|
||||
cfg: CONFIG_READER
|
||||
do
|
||||
create {INI_CONFIG} cfg.make_from_string ("[
|
||||
foo = bar
|
||||
|
||||
[extra]
|
||||
a.b.c = abc
|
||||
|
||||
[expression]
|
||||
text = ${foo}/${a.b.c}
|
||||
]")
|
||||
|
||||
assert ("is valid", not cfg.has_error)
|
||||
assert ("has_item (extra.a.b.c)", cfg.has_item ("extra.a.b.c"))
|
||||
assert ("has_item (extra.a.b.c)", cfg.has_item ("extra.a.b.c"))
|
||||
assert ("has_item (expression.text)", cfg.has_item ("expression.text"))
|
||||
assert ("item (expression.text)", attached cfg.resolved_text_item ("expression.text") as s and then s.same_string_general ("bar/abc"))
|
||||
end
|
||||
|
||||
test_deep_ini
|
||||
local
|
||||
cfg: CONFIG_READER
|
||||
f: RAW_FILE
|
||||
do
|
||||
|
||||
create f.make_with_name ("test_deep.ini")
|
||||
f.create_read_write
|
||||
f.put_string ("[
|
||||
test = extra
|
||||
[new]
|
||||
enabled = true
|
||||
]"
|
||||
)
|
||||
f.close
|
||||
create {INI_CONFIG} cfg.make_from_string ("[
|
||||
foo = bar
|
||||
|
||||
[extra]
|
||||
a.b.c = abc
|
||||
|
||||
[outside]
|
||||
before = include
|
||||
@include=test_deep.ini
|
||||
|
||||
]")
|
||||
f.delete
|
||||
|
||||
assert ("is valid", not cfg.has_error)
|
||||
assert ("has_item (extra.a.b.c)", cfg.has_item ("extra.a.b.c"))
|
||||
assert ("has_item (extra.a.b.c)", cfg.has_item ("extra.a.b.c"))
|
||||
assert ("has_item (outside.new.enabled)", cfg.has_item ("outside.new.enabled"))
|
||||
end
|
||||
|
||||
|
||||
test_json
|
||||
local
|
||||
cfg: CONFIG_READER
|
||||
do
|
||||
create {JSON_CONFIG} cfg.make_from_string ("[
|
||||
{
|
||||
"foo": "bar",
|
||||
"first": {
|
||||
"abc": 1,
|
||||
"def": "and so on",
|
||||
"ghi": "\"path\""
|
||||
},
|
||||
"second": {
|
||||
"this": 1,
|
||||
"is": 2,
|
||||
"the": 3,
|
||||
"end": 4
|
||||
}
|
||||
}
|
||||
]")
|
||||
|
||||
assert ("is valid", not cfg.has_error)
|
||||
assert ("has_item (foo)", cfg.has_item ("foo"))
|
||||
assert ("has_item (first.abc)", cfg.has_item ("first.abc"))
|
||||
assert ("has_item (first.def)", cfg.has_item ("first.def"))
|
||||
assert ("has_item (first.ghi)", cfg.has_item ("first.ghi"))
|
||||
assert ("has_item (second.this)", cfg.has_item ("second.this"))
|
||||
assert ("has_item (second.is)", cfg.has_item ("second.is"))
|
||||
assert ("has_item (second.the)", cfg.has_item ("second.the"))
|
||||
assert ("has_item (second.end)", cfg.has_item ("second.end"))
|
||||
|
||||
assert ("item (foo)", attached cfg.text_item ("foo") as v and then v.same_string_general ("bar"))
|
||||
assert ("item (first.abc)", attached cfg.text_item ("first.abc") as v and then v.same_string_general ("1"))
|
||||
assert ("item (first.def)", attached cfg.text_item ("first.def") as v and then v.same_string_general ("and so on"))
|
||||
assert ("item (first.ghi)", attached cfg.text_item ("first.ghi") as v and then v.same_string_general ("%"path%""))
|
||||
assert ("item (second.this)", attached cfg.text_item ("second.this") as v and then v.same_string_general ("1"))
|
||||
assert ("item (second.is)", attached cfg.text_item ("second.is") as v and then v.same_string_general ("2"))
|
||||
assert ("item (second.the)", attached cfg.text_item ("second.the") as v and then v.same_string_general ("3"))
|
||||
assert ("item (second.end)", attached cfg.text_item ("second.end") as v and then v.same_string_general ("4"))
|
||||
|
||||
if attached cfg.sub_config ("second") as cfg_second then
|
||||
assert ("has_item (is)", cfg_second.has_item ("is"))
|
||||
assert ("item (is)", attached cfg_second.text_item ("is") as v and then v.same_string_general ("2"))
|
||||
|
||||
else
|
||||
assert ("has second", False)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user