Merge branch 'master' of github.com:EiffelWebFramework/EWF
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
note
|
||||
description: "JSON_PRETTY_STRING_VISITOR Generates the JSON-String for a JSON_VALUE"
|
||||
revision: "0.1"
|
||||
|
||||
class
|
||||
JSON_PRETTY_STRING_VISITOR
|
||||
|
||||
inherit
|
||||
JSON_VISITOR
|
||||
|
||||
create
|
||||
make,
|
||||
make_custom
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make (a_output: like output)
|
||||
-- Create a new instance
|
||||
do
|
||||
make_custom (a_output, 1, 1)
|
||||
end
|
||||
|
||||
make_custom (a_output: like output; a_object_count_inlining, a_array_count_inlining: INTEGER)
|
||||
-- Create a new instance
|
||||
do
|
||||
output := a_output
|
||||
create indentation.make_empty
|
||||
indentation_step := "%T"
|
||||
|
||||
object_count_inlining := a_object_count_inlining
|
||||
array_count_inlining := a_array_count_inlining
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
output: STRING_32
|
||||
-- JSON representation
|
||||
|
||||
indentation: like output
|
||||
|
||||
indentation_step: like indentation
|
||||
|
||||
line_number: INTEGER
|
||||
|
||||
indent
|
||||
do
|
||||
indentation.append (indentation_step)
|
||||
end
|
||||
|
||||
exdent
|
||||
do
|
||||
indentation.remove_tail (indentation_step.count)
|
||||
end
|
||||
|
||||
new_line
|
||||
do
|
||||
output.append ("%N")
|
||||
output.append (indentation)
|
||||
line_number := line_number + 1
|
||||
end
|
||||
|
||||
object_count_inlining: INTEGER
|
||||
array_count_inlining: INTEGER
|
||||
|
||||
feature -- Visitor Pattern
|
||||
|
||||
visit_json_array (a_json_array: JSON_ARRAY)
|
||||
-- Visit `a_json_array'.
|
||||
local
|
||||
value: JSON_VALUE
|
||||
l_json_array: ARRAYED_LIST [JSON_VALUE]
|
||||
l_line: like line_number
|
||||
l_multiple_lines: BOOLEAN
|
||||
do
|
||||
l_json_array := a_json_array.array_representation
|
||||
l_multiple_lines := l_json_array.count >= array_count_inlining or across l_json_array as p some attached {JSON_OBJECT} p.item or attached {JSON_ARRAY} p.item end
|
||||
output.append ("[")
|
||||
l_line := line_number
|
||||
indent
|
||||
from
|
||||
l_json_array.start
|
||||
until
|
||||
l_json_array.off
|
||||
loop
|
||||
if
|
||||
line_number > l_line or
|
||||
l_multiple_lines
|
||||
then
|
||||
new_line
|
||||
end
|
||||
value := l_json_array.item
|
||||
value.accept (Current)
|
||||
l_json_array.forth
|
||||
if not l_json_array.after then
|
||||
output.append (", ")
|
||||
end
|
||||
end
|
||||
exdent
|
||||
if
|
||||
line_number > l_line or
|
||||
l_json_array.count >= array_count_inlining
|
||||
then
|
||||
new_line
|
||||
end
|
||||
output.append ("]")
|
||||
end
|
||||
|
||||
visit_json_boolean (a_json_boolean: JSON_BOOLEAN)
|
||||
-- Visit `a_json_boolean'.
|
||||
do
|
||||
output.append (a_json_boolean.item.out)
|
||||
end
|
||||
|
||||
visit_json_null (a_json_null: JSON_NULL)
|
||||
-- Visit `a_json_null'.
|
||||
do
|
||||
output.append ("null")
|
||||
end
|
||||
|
||||
visit_json_number (a_json_number: JSON_NUMBER)
|
||||
-- Visit `a_json_number'.
|
||||
do
|
||||
output.append (a_json_number.item)
|
||||
end
|
||||
|
||||
visit_json_object (a_json_object: JSON_OBJECT)
|
||||
-- Visit `a_json_object'.
|
||||
local
|
||||
l_pairs: HASH_TABLE [JSON_VALUE, JSON_STRING]
|
||||
l_line: like line_number
|
||||
l_multiple_lines: BOOLEAN
|
||||
do
|
||||
l_pairs := a_json_object.map_representation
|
||||
l_multiple_lines := l_pairs.count >= object_count_inlining or across l_pairs as p some attached {JSON_OBJECT} p.item or attached {JSON_ARRAY} p.item end
|
||||
output.append ("{")
|
||||
l_line := line_number
|
||||
indent
|
||||
from
|
||||
l_pairs.start
|
||||
until
|
||||
l_pairs.off
|
||||
loop
|
||||
if
|
||||
line_number > l_line or
|
||||
l_multiple_lines
|
||||
then
|
||||
new_line
|
||||
end
|
||||
l_pairs.key_for_iteration.accept (Current)
|
||||
output.append (": ")
|
||||
l_pairs.item_for_iteration.accept (Current)
|
||||
l_pairs.forth
|
||||
if not l_pairs.after then
|
||||
output.append (", ")
|
||||
end
|
||||
end
|
||||
exdent
|
||||
if
|
||||
line_number > l_line or
|
||||
l_pairs.count >= object_count_inlining
|
||||
then
|
||||
new_line
|
||||
end
|
||||
output.append ("}")
|
||||
end
|
||||
|
||||
visit_json_string (a_json_string: JSON_STRING)
|
||||
-- Visit `a_json_string'.
|
||||
do
|
||||
output.append ("%"")
|
||||
output.append (a_json_string.item)
|
||||
output.append ("%"")
|
||||
end
|
||||
|
||||
end
|
||||
@@ -29,4 +29,4 @@ feature
|
||||
Result.add_converter (jhtc)
|
||||
end
|
||||
|
||||
end -- class SHARED_GOBO_EJSON
|
||||
end -- class SHARED_GOBO_EJSON
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
note
|
||||
description: "A JSON converter for ARRAYED_LIST [ANY]"
|
||||
author: "Paul Cohen"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
file: "$HeadURL: $"
|
||||
|
||||
class JSON_ARRAYED_LIST_CONVERTER
|
||||
|
||||
inherit
|
||||
JSON_LIST_CONVERTER
|
||||
redefine
|
||||
object
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Access
|
||||
|
||||
object: ARRAYED_LIST [detachable ANY]
|
||||
|
||||
feature {NONE} -- Factory
|
||||
|
||||
new_object (nb: INTEGER): like object
|
||||
do
|
||||
create Result.make (nb)
|
||||
end
|
||||
|
||||
end -- class JSON_ARRAYED_LIST_CONVERTER
|
||||
@@ -1,8 +1,8 @@
|
||||
note
|
||||
description: "A JSON converter"
|
||||
author: "Paul Cohen"
|
||||
date: "$Date: $"
|
||||
revision: "$Revision: $"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
file: "$HeadURL: $"
|
||||
|
||||
deferred class JSON_CONVERTER
|
||||
@@ -19,7 +19,7 @@ feature -- Access
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
from_json (j: attached like to_json): detachable like object
|
||||
from_json (j: attached like to_json): detachable like object
|
||||
-- Convert from JSON value.
|
||||
-- Returns Void if unable to convert
|
||||
deferred
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
note
|
||||
description: "A JSON converter for HASH_TABLE [ANY, HASHABLE]"
|
||||
author: "Paul Cohen"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
file: "$HeadURL: $"
|
||||
|
||||
class JSON_HASH_TABLE_CONVERTER
|
||||
@@ -26,7 +26,7 @@ feature -- Access
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
from_json (j: attached like to_json): like object
|
||||
from_json (j: attached like to_json): like object
|
||||
local
|
||||
keys: ARRAY [JSON_STRING]
|
||||
i: INTEGER
|
||||
@@ -41,7 +41,7 @@ feature -- Conversion
|
||||
until
|
||||
i > keys.count
|
||||
loop
|
||||
h ?= json.object (keys [i], void)
|
||||
h ?= json.object (keys [i], Void)
|
||||
check h /= Void end
|
||||
jv := j.item (keys [i])
|
||||
if jv /= Void then
|
||||
|
||||
@@ -1,63 +1,30 @@
|
||||
note
|
||||
description: "A JSON converter for LINKED_LIST [ANY]"
|
||||
author: "Paul Cohen"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
file: "$HeadURL: $"
|
||||
description: "A JSON converter for LINKED_LIST [ANY]"
|
||||
author: "Paul Cohen"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
file: "$HeadURL: $"
|
||||
|
||||
class JSON_LINKED_LIST_CONVERTER
|
||||
|
||||
inherit
|
||||
JSON_CONVERTER
|
||||
JSON_LIST_CONVERTER
|
||||
redefine
|
||||
object
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
create object.make
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
object: LINKED_LIST [detachable ANY]
|
||||
|
||||
feature -- Conversion
|
||||
feature {NONE} -- Factory
|
||||
|
||||
from_json (j: like to_json): detachable like object
|
||||
local
|
||||
i: INTEGER
|
||||
do
|
||||
create Result.make
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
i > j.count
|
||||
loop
|
||||
Result.extend (json.object (j [i], Void))
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
|
||||
to_json (o: like object): JSON_ARRAY
|
||||
local
|
||||
c: ITERATION_CURSOR [detachable ANY]
|
||||
do
|
||||
create Result.make_array
|
||||
from
|
||||
c := o.new_cursor
|
||||
until
|
||||
c.after
|
||||
loop
|
||||
if attached json.value (c.item) as v then
|
||||
Result.add (v)
|
||||
else
|
||||
check attached_value: False end
|
||||
end
|
||||
c.forth
|
||||
end
|
||||
end
|
||||
new_object (nb: INTEGER): like object
|
||||
do
|
||||
create Result.make
|
||||
end
|
||||
|
||||
end -- class JSON_LINKED_LIST_CONVERTER
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
note
|
||||
description: "A JSON converter for LIST [ANY]"
|
||||
author: "Paul Cohen"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
file: "$HeadURL: $"
|
||||
|
||||
deferred class JSON_LIST_CONVERTER
|
||||
|
||||
inherit
|
||||
JSON_CONVERTER
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
object := new_object (0)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
object: LIST [detachable ANY]
|
||||
|
||||
feature {NONE} -- Factory
|
||||
|
||||
new_object (nb: INTEGER): like object
|
||||
deferred
|
||||
ensure
|
||||
Result /= Void
|
||||
end
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
from_json (j: attached like to_json): detachable like object
|
||||
local
|
||||
i: INTEGER
|
||||
do
|
||||
Result := new_object (j.count)
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
i > j.count
|
||||
loop
|
||||
Result.extend (json.object (j [i], Void))
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
|
||||
to_json (o: like object): detachable JSON_ARRAY
|
||||
local
|
||||
c: ITERATION_CURSOR [detachable ANY]
|
||||
jv: detachable JSON_VALUE
|
||||
failed: BOOLEAN
|
||||
do
|
||||
create Result.make_array
|
||||
from
|
||||
c := o.new_cursor
|
||||
until
|
||||
c.after
|
||||
loop
|
||||
jv := json.value (c.item)
|
||||
if jv /= Void then
|
||||
Result.add (jv)
|
||||
else
|
||||
failed := True
|
||||
end
|
||||
c.forth
|
||||
end
|
||||
if failed then
|
||||
Result := Void
|
||||
end
|
||||
end
|
||||
|
||||
end -- class JSON_ARRAYED_LIST_CONVERTER
|
||||
@@ -1,8 +1,8 @@
|
||||
note
|
||||
description: "Core factory class for creating JSON objects and corresponding Eiffel objects."
|
||||
author: "Paul Cohen"
|
||||
date: "$Date: $"
|
||||
revision: "$Revision: $"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
file: "$HeadURL: $"
|
||||
|
||||
class EJSON
|
||||
@@ -69,7 +69,7 @@ feature -- Access
|
||||
elseif attached {STRING_8} an_object as s8 then
|
||||
create {JSON_STRING} Result.make_json (s8)
|
||||
elseif attached {STRING_32} an_object as s32 then
|
||||
create {JSON_STRING} Result.make_json (s32.as_string_8) -- FIXME: need correct convertion/encoding here ...
|
||||
create {JSON_STRING} Result.make_json_from_string_32 (s32)
|
||||
end
|
||||
|
||||
if Result = Void then
|
||||
@@ -253,9 +253,9 @@ feature {NONE} -- Implementation (Exceptions)
|
||||
-- Exception message for failing to convert `a' to a JSON_VALUE.
|
||||
do
|
||||
Result := exception_prefix + "Failed to convert Eiffel object to a JSON_VALUE"
|
||||
if an_object /= Void then
|
||||
Result := ": " + an_object.generator
|
||||
end
|
||||
if an_object /= Void then
|
||||
Result := ": " + an_object.generator
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation (JSON parser)
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
note
|
||||
description: "[
|
||||
Shared factory class for creating JSON objects. Maps JSON
|
||||
objects to ELKS HASH_TABLEs and JSON arrays to ELKS
|
||||
LINKED_LISTs. Use non-conforming inheritance from this
|
||||
class to ensure that your classes share the same
|
||||
JSON_FACTORY instance.
|
||||
Shared factory class for creating JSON objects. Maps JSON
|
||||
objects to ELKS HASH_TABLEs and JSON arrays to ELKS
|
||||
LINKED_LISTs. Use non-conforming inheritance from this
|
||||
class to ensure that your classes share the same
|
||||
JSON_FACTORY instance.
|
||||
]"
|
||||
author: "Paul Cohen"
|
||||
date: "$Date: $"
|
||||
revision: "$Revision: $"
|
||||
date: "$Date$"
|
||||
revision: "$Revision: 89185 $"
|
||||
file: "$HeadURL: $"
|
||||
|
||||
class SHARED_EJSON
|
||||
@@ -17,16 +17,22 @@ feature
|
||||
|
||||
json: EJSON
|
||||
-- A shared EJSON instance with default converters for
|
||||
-- DS_LINKED_LIST [ANY] and DS_HASH_TABLE [ANY, HASHABLE]
|
||||
--LINKED_LIST [ANY] and HASH_TABLE [ANY, HASHABLE]
|
||||
local
|
||||
jalc: JSON_ARRAYED_LIST_CONVERTER
|
||||
jllc: JSON_LINKED_LIST_CONVERTER
|
||||
jhtc: JSON_HASH_TABLE_CONVERTER
|
||||
once
|
||||
create Result
|
||||
|
||||
create jalc.make
|
||||
Result.add_converter (jalc)
|
||||
|
||||
create jllc.make
|
||||
Result.add_converter (jllc)
|
||||
|
||||
create jhtc.make
|
||||
Result.add_converter (jhtc)
|
||||
end
|
||||
|
||||
end -- class SHARED_EJSON
|
||||
end -- class SHARED_EJSON
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
note
|
||||
description: "A JSON converter for AUTHOR"
|
||||
author: "Paul Cohen"
|
||||
date: "$Date: 2010-03-08 20:46:59 -0300 (Mon, 08 Mar 2010) $"
|
||||
revision: "$Revision: 82 $"
|
||||
file: "$HeadURL: https://svn.origo.ethz.ch/ejson/branches/POC-converters-factory/test/json_author_converter.e $"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class JSON_AUTHOR_CONVERTER
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
note
|
||||
description: "A JSON converter for BOOK_COLLECTION"
|
||||
author: "Paul Cohen"
|
||||
date: "$Date: 2010-03-08 20:46:59 -0300 (Mon, 08 Mar 2010) $"
|
||||
revision: "$Revision: 82 $"
|
||||
file: "$HeadURL: https://svn.origo.ethz.ch/ejson/branches/POC-converters-factory/test/json_book_collection_converter.e $"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class JSON_BOOK_COLLECTION_CONVERTER
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
note
|
||||
description: "A JSON converter for BOOK"
|
||||
author: "Paul Cohen"
|
||||
date: "$Date: 2010-03-08 20:46:59 -0300 (Mon, 08 Mar 2010) $"
|
||||
revision: "$Revision: 82 $"
|
||||
file: "$HeadURL: https://svn.origo.ethz.ch/ejson/branches/POC-converters-factory/test/json_book_converter.e $"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class JSON_BOOK_CONVERTER
|
||||
|
||||
|
||||
@@ -632,7 +632,7 @@ feature -- Test
|
||||
jrep := "%"foo\\bar%""
|
||||
create parser.make_parser (jrep)
|
||||
if attached {JSON_STRING} parser.parse as jstring then
|
||||
assert ("unescaped string %"foo\\bar%" to %"foo\bar%"", jstring.unescaped_string_8.same_string ("foo\bar"))
|
||||
assert ("unescaped string %"foo\\bar%" to %"foo\bar%"", jstring.unescaped_string.same_string ("foo\bar"))
|
||||
end
|
||||
|
||||
create js.make_json_from_string_32 ({STRING_32}"%/20320/%/22909/")
|
||||
|
||||
@@ -1,16 +1,26 @@
|
||||
## Eiffel-Web-Framework ##
|
||||
# Eiffel-Web-Framework #
|
||||
|
||||
## Location ##
|
||||
The official documentation/wiki is located at https://github.com/EiffelWebFramework/EWF/wiki , if you are visiting a "clone/fork", please always check the [[official wiki|https://github.com/EiffelWebFramework/EWF/wiki]].
|
||||
|
||||
|
||||
## Organization ##
|
||||
- Mailing list: please visit and subscribe to the mailing list page [[http://groups.google.com/group/eiffel-web-framework]] 
|
||||
- Most of the topics are discussed on the mailing list (google group).
|
||||
- For time to time we have web meeting, and less frequently physical meetings that occurs usually during other Eiffel related events.
|
||||
|
||||
- See also
|
||||
- You want to contribute or follow the progress/discussion, see the [[collaboration page| Community-collaboration]]
|
||||
## Documentation ##
|
||||
- to redo
|
||||
|
||||
## Contributions ##
|
||||
- You want to contribute or follow the progress/discussion, see the [[collaboration page| Community-collaboration]]
|
||||
- Potential tasks/projects on EWF: [[Projects page| Projects]]
|
||||
|
||||
## See also ##
|
||||
- [[list of tasks, and a potential roadmap| Tasks-Roadmap]]
|
||||
- [[General source structure of this project| Source-structure]]
|
||||
- EWSGI: [[Eiffel Web Server Gateway Interface| EWSGI]]
|
||||
- [[Overview of the server side architecture| Spec-Server-Architecture]]
|
||||
- This project is also a collection of [[Libraries]] related to the Web
|
||||
|
||||
## Note ##
|
||||
- This wiki needs to be updated, in the meantime, please have a look at the presentation: https://docs.google.com/presentation/pub?id=1GPFv6aHhTjFSLMnlAt-J4WeIHSGfHdB42dQxmOVOH8s&start=false&loop=false&delayms=3000
|
||||
|
||||
14
doc/wiki/Projects-new-suggestions.md
Normal file
14
doc/wiki/Projects-new-suggestions.md
Normal file
@@ -0,0 +1,14 @@
|
||||
Use this to suggest new projects, or request features.
|
||||
The content of this page will be moved to the main [[Projects]] page for time to time.
|
||||
For any entry, please use this template
|
||||
|
||||
----
|
||||
|
||||
## Short title
|
||||
* _Suggested by **your name**_": so that we know who suggested a feature.
|
||||
* **Requirement**: ... if any, otherwise remove this line
|
||||
* _Description_: ... a few lines to describe the project
|
||||
* _References_: ... if any, otherwise remove this line
|
||||
|
||||
----
|
||||
|
||||
231
doc/wiki/Projects.md
Normal file
231
doc/wiki/Projects.md
Normal file
@@ -0,0 +1,231 @@
|
||||
This page lists potential projects on EWF, this is open for contribution.
|
||||
If you are a student, don't hesitate to pick one, or even suggest a new project, or a project being a merge of several, in any case, you will get close support from EWF's team.
|
||||
|
||||
----
|
||||
# Study/Analysis/Documentation
|
||||
|
||||
## Evaluate EWF according to the following constraints ...
|
||||
* _Suggested by **Javier**_
|
||||
* _Description_: According to http://www.amundsen.com/blog/archives/1130 , evaluate the current design of EWF to see if this match the different points. An other option would be to take the following REST implementation toolkit as a guide to evaluate EWF http://code.google.com/p/implementing-rest/wiki/RESTImplementationToolkit.
|
||||
|
||||
## Road to Hypermedia API
|
||||
* _Suggested by **Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_:
|
||||
* _Description_: describe differents types of Web API, and how you can build them using EWF. Describing Pros and Cons. This should be on http://martinfowler.com/articles/richardsonMaturityModel.html
|
||||
|
||||
## Build a video to demonstrate how an Hypermedia API works, and how to build it using EWF
|
||||
* _Suggested by **Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: produce a audio+video+slide that demonstrates how to build an hypermedia API using EWF. This could be based on upcoming "graphviz server" example, or an extension of existing RestBucksCRUD example, or any new example.
|
||||
|
||||
----
|
||||
# Works related to EWF / framework / tools
|
||||
|
||||
## Improve EWF
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Improve existing EWF source, this is a permanent task for EWF, and this can be code, documentation, tests, ... Among others , here is a list of needed effort:
|
||||
** Improve encoding support
|
||||
*∗ Better MIME handler
|
||||
** _Support for configuration _
|
||||
** Ready to use logging facilities
|
||||
** Smart handler for HEAD or similar
|
||||
** Adding component to ease the caching functionalities
|
||||
** Adding Session support
|
||||
** URL rewriting ?
|
||||
** Mass testing
|
||||
** ...
|
||||
|
||||
## Eiffel Web Nino
|
||||
* _Suggested by **Javier & Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Currently Eiffel Web Nino, is a standalone httpd server written in Eiffel. It is great for development, or embedding httpd component in application. However there are room for improvement so that one can also use it as replacement for apache, iis, ... To reach this state, here are a list of task that should be achieved:
|
||||
** Implement persistent connection
|
||||
** Complete implementation of Eiffel Web Nino using pool of threads
|
||||
** Complete migration of Eiffel Web Nino to SCOOP
|
||||
** Improve Nino to become a real solution to host any web services/sites
|
||||
** ...
|
||||
|
||||
## New EWF connectors
|
||||
* _Suggested by **Jocelyn & Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: EWF is relying on the notion of "connector" to achieve portability on various platform and underlying httpd server, currently EWF support any CGI or libFCGI system (i.e apache, IIS, ...), and provide a standalone version thanks to Eiffel Web Nino. The goal now, would be to support specific connector for:
|
||||
** LightHTTP (http://www.lighttpd.net/)
|
||||
** nginx (http://nginx.org/en/)
|
||||
|
||||
## Concurrenty and EWF
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Check that EWF is compliant with concurrency (Eiffel Thread, and SCOOP), and provide an example using concurrency.
|
||||
|
||||
## Design and build something like Ruby on Rails or Grails
|
||||
* _Suggested by **Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Using EWF, design and build the set of tools to provide a conventional MVC to create Web sites. This could be useful even if this is not the taste of everyone.
|
||||
|
||||
## Provide a Websocket implementation
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Provide an implementation of websocket with EWF and eventually Eiffel Web Nino, then demonstrate it on a simple example. WebSocket is a web technology providing for bi-directional, full-duplex communications channels over a single TCP connection.
|
||||
* See http://en.wikipedia.org/wiki/Websocket
|
||||
|
||||
----
|
||||
# Usage of EWF
|
||||
|
||||
## HAL browser
|
||||
* _Suggested by **Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Build a HAL browser to discover an API using HAL mediatype. The browser will be able to follow the links, and display the transmitted data. This could be a vision2 application inspired by http://haltalk.herokuapp.com/explorer/hal_browser.html#/. HAL stands for Hypertext Application Language see http://stateless.co/hal_specification.html.
|
||||
|
||||
## Collection-JSON browser
|
||||
* _Suggested by **Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Build a Collection/JSON browser to discover an API using Collection/JSON mediatype. The browser will be able to follow the links, and display the transmitted data. This could be a vision2 application inspired by http://haltalk.herokuapp.com/explorer/hal_browser.html#/. Collection+JSON is a JSON-based read/write hypermedia-type, see http://www.amundsen.com/media-types/collection/
|
||||
|
||||
## Build a simple CMS with EWF
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Using EWF, Build a simple CMS (Content Management System) framework and then an example. It should provide common features such as:
|
||||
- user management (register, login, lost password -> send email)
|
||||
- page editing
|
||||
- blog
|
||||
- template / theme
|
||||
- persistency / storage / ...
|
||||
- extension at compilation time
|
||||
* The result should be usable by any user to build his own CMS website, and extend it easily.
|
||||
|
||||
## Build P2P connector
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Imagine you want to publish a website (or web service, API) running on your machine (behind firewall). One would need to initiate the connection via a public website, this is common for P2P software such as remote assistance (i.e: join.me, teamviewer, showmypc, ...)
|
||||
|
||||
----
|
||||
# Libraries
|
||||
|
||||
## Hypermedia API library to work with XHTML
|
||||
* _Suggested by **Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Use XHTML as a media type to for hypermedia API. See http://codeartisan.blogspot.com.ar/2012/07/using-html-as-media-type-for-your-api.html
|
||||
|
||||
## Add support for Mediatype such as RSS, ATOM, ...
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: In addition to JSON, HAL, Collection+JSON, XHTML, application might want to support (read and write) standard media type such as RSS, ATOM, ...
|
||||
|
||||
## Security: provide popular authentication mechanisms
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Any web service, web site, API need a reliable authentication mechanism, the could be on the server side or the client side to build mashup service (integrate with other web API such as google, flicker, ...). So far, EWF provides only basic HTTP Authorization, and application would need more solutions such as :
|
||||
- OAuth: consumer and provider
|
||||
- OpenID
|
||||
- Google Connect
|
||||
- Facebook Connect
|
||||
* The goal is to provide component to consume other popular API/service, but also component for your own service so that other can consume it.
|
||||
|
||||
## Security: provide popular authentication mechanisms
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Any web service, web site, API need a reliable authentication mechanism, the could be on the server side or the client side to build mashup service (integrate with other web API such as google, flicker, ...). So far, EWF provides only basic HTTP Authorization, and application would need more solutions such as :
|
||||
- OAuth: consumer and provider
|
||||
- OpenID
|
||||
- Google Connect
|
||||
- Facebook Connect
|
||||
* The goal is to provide component to consume other popular API/service, but also component for your own service so that other can consume it.
|
||||
|
||||
## Provide a SSO (Single Sign On) implementation (server, and clients)
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Design and build a Single Sign On implementation for Eiffel. That should include the authentication server, and at least one Eiffel client component (it would be convenient to also provide php, js, ...). In the same spirit, having Eiffel client for popular SSO server would be appreciated as well.
|
||||
* _Reference_:
|
||||
- http://en.wikipedia.org/wiki/Single_sign-on
|
||||
- http://en.wikipedia.org/wiki/List_of_single_sign-on_implementations
|
||||
|
||||
## library: Template engine
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Get inspired by any existing template engine, and build one for Eiffel, this should be easily usable within a web application. This could be inspired, or implementation of standard template engine, this way people can reuse existing content, or migrate easily their application to EWF. For inspiration, one can look at:
|
||||
- http://www.smarty.net/
|
||||
- http://mustache.github.com/
|
||||
- http://en.wikipedia.org/wiki/Template_engine_(web) ... they are plenty of them, a comparison of the different engine would help.
|
||||
* This is not specific to EWF, but it will be very useful in website context.
|
||||
|
||||
## library: Wikitext, markdown parser and render engine
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Build component to support (read and write, and why not convert), lightweight markup language (see http://en.wikipedia.org/wiki/Lightweight_markup_language) such as wikitext, markdown, and other. The component should be able to read/scan, but also produce an HTML output. Focus first on wikitext, and markdown since they seems to be the most popular.
|
||||
* Then , a nice addition would be to render those lightweight markup lang into Vision2 widget (not related to EWF, but could be useful to build (editor) desktop application)
|
||||
|
||||
## library: Web component to build HTML5 widget
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Build set of Eiffel components to ease development of websites. First this should be based on HTML5. Idea for components:
|
||||
- table widget (with sorting ...)
|
||||
- suggestive typing widget
|
||||
- tab ...
|
||||
- WYSIWYG textarea widget (could reuse existing Javascript solution TinyMCE, CKEditor, OpenWysiwyg, ...)
|
||||
- ...
|
||||
|
||||
----
|
||||
# Clients
|
||||
|
||||
## Libraries: Reusable Client Design based on J.Moore Presentation
|
||||
* _Suggested by **Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: TODO
|
||||
* Generic client that can be customized (see design in slide 12)
|
||||
* http://s3.amazonaws.com/cimlabs/Oredev-Hypermedia-APIs.pdf
|
||||
* video http://vimeo.com/20781278
|
||||
|
||||
## Create a Client Cache based on Apache commons Client Cache.
|
||||
* _Suggested by **Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: TODO
|
||||
* http://hc.apache.org/httpcomponents-client-ga/httpclient-cache/index.html
|
||||
* http://labs.xfinity.com/benchmarking-the-httpclient-caching-module
|
||||
|
||||
## Add SSL support to Eiffel Net
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Currently Eiffel Net does not provide any support to SSL (thus no HTTPS). For now Eiffel application often use the Eiffel cURL wrapper which provide SSL, but it would be more convenient to use directly Eiffel Net. Then find solution to add SSL support to EiffelNet, or to extend EiffelNet with an EiffelNet+OpenSSL solution, or other.
|
||||
|
||||
## Build clients to consume popular RESTful APIs
|
||||
* _Suggested by **Jocelyn & Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Build Eiffel libraries to consume popular web APIs, such as:
|
||||
- Google Discovery APIs
|
||||
- Twitter
|
||||
- Facebook
|
||||
- Github
|
||||
- Flickr
|
||||
- ... etc
|
||||
* This should reuse and improve the "http_client" provided by EWF. Eventually also write the EiffelNet implementation to be independant from cURL
|
||||
* **Requirement**: OAuth client eiffel component
|
||||
|
||||
----
|
||||
# Feel free to add new idea below this line
|
||||
----
|
||||
Use the following page [[Projects new suggestions]] to suggest new project, or request a feature.
|
||||
33
examples/filter/filter-safe.ecf
Normal file
33
examples/filter/filter-safe.ecf
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-9-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-9-0 http://www.eiffel.com/developers/xml/configuration-1-9-0.xsd" name="filter" uuid="7C9887BD-4AE4-47F2-A0AA-4BBB6736D433">
|
||||
<target name="filter">
|
||||
<root class="FILTER_SERVER" feature="make"/>
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/\.git$</exclude>
|
||||
<exclude>/\.svn$</exclude>
|
||||
</file_rule>
|
||||
<option debug="true" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
||||
<debug name="nino" enabled="true"/>
|
||||
<assertions precondition="true" postcondition="true" invariant="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="connector_nino" location="..\..\library\server\ewsgi\connectors\nino\nino-safe.ecf" readonly="false">
|
||||
<option debug="true">
|
||||
<debug name="nino" enabled="true"/>
|
||||
</option>
|
||||
</library>
|
||||
<library name="default_nino" location="..\..\library\server\wsf\default\nino-safe.ecf" readonly="false"/>
|
||||
<library name="eel" location="..\..\contrib\ise_library\text\encryption\eel\eel-safe.ecf" readonly="false"/>
|
||||
<library name="encoder" location="..\..\library\text\encoder\encoder-safe.ecf" readonly="false"/>
|
||||
<library name="http" location="../../library/network/protocol/http/http-safe.ecf" readonly="false"/>
|
||||
<library name="json" location="..\..\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
|
||||
<library name="uri_template" location="../../library/text/parser/uri_template/uri_template-safe.ecf" readonly="false"/>
|
||||
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf" readonly="false"/>
|
||||
<library name="wsf_extension" location="..\..\library\server\wsf_extension\wsf_extension-safe.ecf" readonly="false"/>
|
||||
<library name="http_authorization" location="..\..\library\server\authentication\http_authorization\http_authorization-safe.ecf" readonly="false"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||||
<cluster name="src" location="src\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
4
examples/filter/readme.md
Normal file
4
examples/filter/readme.md
Normal file
@@ -0,0 +1,4 @@
|
||||
Filter example
|
||||
|
||||
To test the example, you can just run in a terminal:
|
||||
> curl -u foo:bar http://localhost:9090/user/1 -v
|
||||
30
examples/filter/src/database/database_api.e
Normal file
30
examples/filter/src/database/database_api.e
Normal file
@@ -0,0 +1,30 @@
|
||||
note
|
||||
description: "Summary description for {DATABASE_API}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
DATABASE_API
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make
|
||||
local
|
||||
l_user: USER
|
||||
do
|
||||
create users.make (10)
|
||||
create l_user.make (1, "foo", "bar")
|
||||
users.put (l_user, l_user.id)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
users: HASH_TABLE [USER, INTEGER]
|
||||
|
||||
;note
|
||||
copyright: "2011-2011, Javier Velilla and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
20
examples/filter/src/database/shared_database_api.e
Normal file
20
examples/filter/src/database/shared_database_api.e
Normal file
@@ -0,0 +1,20 @@
|
||||
note
|
||||
description: "Summary description for {SHARED_DATABASE_API}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
SHARED_DATABASE_API
|
||||
|
||||
feature -- Access
|
||||
|
||||
db_access: DATABASE_API
|
||||
once
|
||||
create Result.make
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Javier Velilla and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
52
examples/filter/src/domain/json_user_converter.e
Normal file
52
examples/filter/src/domain/json_user_converter.e
Normal file
@@ -0,0 +1,52 @@
|
||||
note
|
||||
description: "JSON user converter."
|
||||
author: "Olivier Ligot"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
JSON_USER_CONVERTER
|
||||
|
||||
inherit
|
||||
JSON_CONVERTER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
create object.make (0, "", "")
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
object: USER
|
||||
|
||||
value: detachable JSON_OBJECT
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
from_json (j: attached like value): detachable like object
|
||||
-- Convert from JSON value.
|
||||
do
|
||||
end
|
||||
|
||||
to_json (o: like object): like value
|
||||
-- Convert to JSON value.
|
||||
do
|
||||
create Result.make
|
||||
Result.put (json.value (o.id), id_key)
|
||||
Result.put (json.value (o.name), name_key)
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
id_key: STRING = "id"
|
||||
name_key: STRING = "name"
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Javier Velilla and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
40
examples/filter/src/domain/user.e
Normal file
40
examples/filter/src/domain/user.e
Normal file
@@ -0,0 +1,40 @@
|
||||
note
|
||||
description: "User."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
USER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (an_id: INTEGER; a_name, a_password: STRING)
|
||||
do
|
||||
id := an_id
|
||||
name := a_name
|
||||
password := a_password
|
||||
ensure
|
||||
id_set: id = an_id
|
||||
name_set: name = a_name
|
||||
password_set: password = a_password
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
id: INTEGER
|
||||
-- Identifier
|
||||
|
||||
name: STRING
|
||||
-- Name
|
||||
|
||||
password: STRING
|
||||
-- Password
|
||||
|
||||
;note
|
||||
copyright: "2011-2011, Javier Velilla and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
50
examples/filter/src/filter/authentication_filter.e
Normal file
50
examples/filter/src/filter/authentication_filter.e
Normal file
@@ -0,0 +1,50 @@
|
||||
note
|
||||
description: "Authentication filter."
|
||||
author: "Olivier Ligot"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
AUTHENTICATION_FILTER [C -> WSF_URI_TEMPLATE_HANDLER_CONTEXT]
|
||||
|
||||
inherit
|
||||
WSF_FILTER_HANDLER [C]
|
||||
|
||||
SHARED_DATABASE_API
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
execute (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute the filter
|
||||
local
|
||||
l_auth: HTTP_AUTHORIZATION
|
||||
do
|
||||
create l_auth.make (req.http_authorization)
|
||||
if (attached l_auth.type as l_auth_type and then l_auth_type.is_equal ("basic")) and
|
||||
attached Db_access.users.item (1) as l_user and then
|
||||
(attached l_auth.login as l_auth_login and then l_auth_login.is_equal (l_user.name)
|
||||
and attached l_auth.password as l_auth_password and then l_auth_password.is_equal (l_user.password)) then
|
||||
execute_next (ctx, req, res)
|
||||
else
|
||||
handle_unauthorized ("Unauthorized", ctx, req, res)
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
handle_unauthorized (a_description: STRING; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Handle forbidden.
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
do
|
||||
create h.make
|
||||
h.put_content_type_text_plain
|
||||
h.put_content_length (a_description.count)
|
||||
h.put_current_date
|
||||
h.put_header_key_value ({HTTP_HEADER_NAMES}.header_www_authenticate, "Basic realm=%"User%"")
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.unauthorized)
|
||||
res.put_header_text (h.string)
|
||||
res.put_string (a_description)
|
||||
end
|
||||
|
||||
end
|
||||
40
examples/filter/src/filter/logging_filter.e
Normal file
40
examples/filter/src/filter/logging_filter.e
Normal file
@@ -0,0 +1,40 @@
|
||||
note
|
||||
description: "Logging filter."
|
||||
author: "Olivier Ligot"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
LOGGING_FILTER
|
||||
|
||||
inherit
|
||||
WSF_FILTER
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute the filter
|
||||
local
|
||||
l_user_agent: STRING
|
||||
l_date: DATE_TIME
|
||||
do
|
||||
if attached req.http_user_agent as ua then
|
||||
l_user_agent := ua.as_string_8
|
||||
else
|
||||
l_user_agent := "-"
|
||||
end
|
||||
create l_date.make_now
|
||||
io.put_string ("[" + l_date.formatted_out (Date_time_format) + "] %"" + req.request_method + " " + req.request_uri
|
||||
+ " " + {HTTP_CONSTANTS}.http_version_1_1 + "%" " + res.status_code.out + " " + l_user_agent)
|
||||
io.put_new_line
|
||||
execute_next (req, res)
|
||||
end
|
||||
|
||||
feature -- Constants
|
||||
|
||||
Date_time_format: STRING = "yyyy/[0]mm/[0]dd [0]hh:[0]mi:[0]ss.ff3"
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Javier Velilla and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
109
examples/filter/src/filter_server.e
Normal file
109
examples/filter/src/filter_server.e
Normal file
@@ -0,0 +1,109 @@
|
||||
note
|
||||
description : "Filter example."
|
||||
author : "Olivier Ligot"
|
||||
date : "$Date$"
|
||||
revision : "$Revision$"
|
||||
|
||||
class
|
||||
FILTER_SERVER
|
||||
|
||||
inherit
|
||||
ANY
|
||||
|
||||
WSF_URI_TEMPLATE_FILTERED_SERVICE
|
||||
|
||||
WSF_HANDLER_HELPER
|
||||
|
||||
WSF_DEFAULT_SERVICE
|
||||
|
||||
SHARED_EJSON
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
initialize_filter
|
||||
initialize_json
|
||||
set_service_option ("port", 9090)
|
||||
make_and_launch
|
||||
end
|
||||
|
||||
create_filter
|
||||
-- Create `filter'
|
||||
local
|
||||
l_router: WSF_URI_TEMPLATE_ROUTER
|
||||
l_authentication_filter: AUTHENTICATION_FILTER [WSF_URI_TEMPLATE_HANDLER_CONTEXT]
|
||||
l_user_filter: USER_HANDLER [WSF_URI_TEMPLATE_HANDLER_CONTEXT]
|
||||
l_user_handler: WSF_HANDLER [WSF_URI_TEMPLATE_HANDLER_CONTEXT]
|
||||
l_routing_filter: WSF_ROUTING_FILTER [WSF_HANDLER [WSF_URI_TEMPLATE_HANDLER_CONTEXT], WSF_URI_TEMPLATE_HANDLER_CONTEXT]
|
||||
do
|
||||
create l_router.make (1)
|
||||
create l_authentication_filter
|
||||
create l_user_filter
|
||||
l_authentication_filter.set_next (l_user_filter)
|
||||
l_user_handler := l_authentication_filter
|
||||
l_router.map_with_request_methods ("/user/{userid}", l_user_handler, << "GET" >>)
|
||||
create l_routing_filter.make (l_router)
|
||||
l_routing_filter.set_execute_default_action (agent execute_default)
|
||||
filter := l_routing_filter
|
||||
end
|
||||
|
||||
setup_filter
|
||||
-- Setup `filter'
|
||||
local
|
||||
l_logging_filter: LOGGING_FILTER
|
||||
do
|
||||
create l_logging_filter
|
||||
filter.set_next (l_logging_filter)
|
||||
end
|
||||
|
||||
initialize_json
|
||||
-- Initialize `json'.
|
||||
do
|
||||
json.add_converter (create {JSON_USER_CONVERTER}.make)
|
||||
end
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
filter.execute (req, res)
|
||||
end
|
||||
|
||||
execute_default (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- I'm using this method to handle the method not allowed response
|
||||
-- in the case that the given uri does not have a corresponding http method
|
||||
-- to handle it.
|
||||
local
|
||||
h : HTTP_HEADER
|
||||
l_description : STRING
|
||||
l_api_doc : STRING
|
||||
do
|
||||
if req.content_length_value > 0 then
|
||||
req.input.read_string (req.content_length_value.as_integer_32)
|
||||
end
|
||||
create h.make
|
||||
h.put_content_type_text_plain
|
||||
l_api_doc := "%NPlease check the API%NURI:/user/{userid} METHOD: GET%N"
|
||||
l_description := req.request_method + req.request_uri + " is not allowed" + "%N" + l_api_doc
|
||||
h.put_content_length (l_description.count)
|
||||
h.put_current_date
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.method_not_allowed)
|
||||
res.put_header_text (h.string)
|
||||
res.put_string (l_description)
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Javier Velilla 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
|
||||
86
examples/filter/src/resource/user_handler.e
Normal file
86
examples/filter/src/resource/user_handler.e
Normal file
@@ -0,0 +1,86 @@
|
||||
note
|
||||
description: "User handler."
|
||||
author: "Olivier Ligot"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
USER_HANDLER [C -> WSF_HANDLER_CONTEXT]
|
||||
|
||||
inherit
|
||||
WSF_FILTER_HANDLER [C]
|
||||
|
||||
WSF_RESOURCE_HANDLER_HELPER [C]
|
||||
redefine
|
||||
do_get
|
||||
end
|
||||
|
||||
SHARED_DATABASE_API
|
||||
|
||||
SHARED_EJSON
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
execute (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute request handler
|
||||
do
|
||||
execute_methods (ctx, req, res)
|
||||
end
|
||||
|
||||
do_get (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Using GET to retrieve resource information.
|
||||
-- If the GET request is SUCCESS, we response with
|
||||
-- 200 OK, and a representation of the user
|
||||
-- If the GET request is not SUCCESS, we response with
|
||||
-- 404 Resource not found
|
||||
local
|
||||
id : STRING
|
||||
do
|
||||
if attached req.orig_path_info as orig_path then
|
||||
id := get_user_id_from_path (orig_path)
|
||||
if attached retrieve_user (id) as l_user then
|
||||
compute_response_get (ctx, req, res, l_user)
|
||||
else
|
||||
handle_resource_not_found_response ("The following resource " + orig_path + " is not found ", ctx, req, res)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
compute_response_get (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE; l_user : USER)
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
l_msg : STRING
|
||||
do
|
||||
create h.make
|
||||
h.put_content_type_application_json
|
||||
if attached {JSON_VALUE} json.value (l_user) as jv then
|
||||
l_msg := jv.representation
|
||||
h.put_content_length (l_msg.count)
|
||||
if attached req.request_time as time then
|
||||
h.add_header ("Date:" + time.formatted_out ("ddd,[0]dd mmm yyyy [0]hh:[0]mi:[0]ss.ff2") + " GMT")
|
||||
end
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||
res.put_header_text (h.string)
|
||||
res.put_string (l_msg)
|
||||
end
|
||||
end
|
||||
|
||||
get_user_id_from_path (a_path: READABLE_STRING_32) : STRING
|
||||
do
|
||||
Result := a_path.split ('/').at (3)
|
||||
end
|
||||
|
||||
retrieve_user (id: STRING) : detachable USER
|
||||
-- Retrieve the user by id if it exist, in other case, Void
|
||||
do
|
||||
if id.is_integer and then Db_access.users.has (id.to_integer) then
|
||||
Result := db_access.users.item (id.to_integer)
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Javier Velilla and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -17,10 +17,15 @@ or go to [[step_3.wiki|step 3]]
|
||||
** This is used to build the application in a portable manner, but for this compilation, it uses Eiffel Web Nino as connector.
|
||||
** We use Eiffel Web Nino for this tutorial, because there is no need to configure any apache, iis, and so on. And it is convenient to execute inside EiffelStudio
|
||||
|
||||
* To see the result, you should open http://localhost/ on your web browser. Note if the application is using another port such as 9999, you should open http://localhost:9999/
|
||||
|
||||
* You will find inside [[step_2]] the "hello" project
|
||||
** target "hello" provides a very simple implementation (But by default, it is using port 80 with Eiffel Web Nino, which might already be busy by other application)
|
||||
** target "hello_custom" which uses almost the same code, but in addition, you can use the ewf.ini file to precise the port number (9999 for this example)
|
||||
|
||||
* To see the result, open http://localhost/ in a web browser.
|
||||
* Note, if the application is using port different from 80, such as 9999, open http://localhost:9999/
|
||||
|
||||
* Eiffel code
|
||||
class
|
||||
HELLO_APPLICATION
|
||||
|
||||
@@ -390,23 +390,33 @@ feature -- Date
|
||||
put_utc_date (create {DATE_TIME}.make_now_utc)
|
||||
end
|
||||
|
||||
put_utc_date (dt: DATE_TIME)
|
||||
put_utc_date (a_utc_date: DATE_TIME)
|
||||
-- Put UTC date time `dt' with "Date" header
|
||||
do
|
||||
put_date (date_to_rfc1123_http_date_format (dt))
|
||||
put_date (date_to_rfc1123_http_date_format (a_utc_date))
|
||||
end
|
||||
|
||||
put_last_modified (dt: DATE_TIME)
|
||||
put_last_modified (a_utc_date: DATE_TIME)
|
||||
-- Put UTC date time `dt' with "Date" header
|
||||
do
|
||||
put_header_key_value ({HTTP_HEADER_NAMES}.header_last_modified, date_to_rfc1123_http_date_format (dt))
|
||||
put_header_key_value ({HTTP_HEADER_NAMES}.header_last_modified, date_to_rfc1123_http_date_format (a_utc_date))
|
||||
end
|
||||
|
||||
feature -- Others
|
||||
feature -- Others
|
||||
|
||||
put_expires (n: INTEGER)
|
||||
put_expires (sec: INTEGER)
|
||||
do
|
||||
put_header_key_value ("Expires", n.out)
|
||||
put_expires_string (sec.out)
|
||||
end
|
||||
|
||||
put_expires_string (s: STRING)
|
||||
do
|
||||
put_header_key_value ("Expires", s)
|
||||
end
|
||||
|
||||
put_expires_date (a_utc_date: DATE_TIME)
|
||||
do
|
||||
put_header_key_value ("Expires", date_to_rfc1123_http_date_format (a_utc_date))
|
||||
end
|
||||
|
||||
put_cache_control (s: READABLE_STRING_8)
|
||||
|
||||
27
library/server/wsf/src/filter/README.md
Normal file
27
library/server/wsf/src/filter/README.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Introduction
|
||||
|
||||
The basic idea of a filter is to pre-process incoming data and post-process outgoing data.
|
||||
Filters are part of a filter chain, thus following the [chain of responsability design pattern](http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern).
|
||||
|
||||
Each filter decides to call the next filter or not.
|
||||
|
||||
# Levels
|
||||
|
||||
In EWF, there are two levels of filters.
|
||||
|
||||
## WSF_FILTER
|
||||
|
||||
Typical examples of such filters are: logging, compression, routing (WSF_ROUTING_FILTER), ...
|
||||
|
||||
## WSF_FILTER_HANDLER
|
||||
|
||||
Handler that can also play the role of a filter.
|
||||
|
||||
Typical examples of such filters are: authentication, ...
|
||||
|
||||
# References
|
||||
|
||||
Filters (also called middelwares) in other environments:
|
||||
* in Python: http://www.wsgi.org/en/latest/libraries.html
|
||||
* in Node.js: http://expressjs.com/guide.html#middleware
|
||||
* in Apache: http://httpd.apache.org/docs/2.2/en/filter.html
|
||||
52
library/server/wsf/src/filter/wsf_filter.e
Normal file
52
library/server/wsf/src/filter/wsf_filter.e
Normal file
@@ -0,0 +1,52 @@
|
||||
note
|
||||
description: "Objects than can pre-process incoming data and post-process outgoing data."
|
||||
author: "Olivier Ligot"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
WSF_FILTER
|
||||
|
||||
feature -- Access
|
||||
|
||||
next: detachable WSF_FILTER
|
||||
-- Next filter
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_next (a_next: WSF_FILTER)
|
||||
-- Set `next' to `a_next'
|
||||
do
|
||||
next := a_next
|
||||
ensure
|
||||
next_set: next = a_next
|
||||
end
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute the filter.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
execute_next (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute the `next' filter.
|
||||
do
|
||||
if attached next as n then
|
||||
n.execute (req, res)
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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
|
||||
51
library/server/wsf/src/filter/wsf_filter_handler.e
Normal file
51
library/server/wsf/src/filter/wsf_filter_handler.e
Normal file
@@ -0,0 +1,51 @@
|
||||
note
|
||||
description: "[
|
||||
Handler that can also play the role of a filter, i.e.
|
||||
than can pre-process incoming data and post-process outgoing data.
|
||||
]"
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
WSF_FILTER_HANDLER [C -> WSF_HANDLER_CONTEXT]
|
||||
|
||||
inherit
|
||||
WSF_HANDLER [C]
|
||||
|
||||
feature -- Access
|
||||
|
||||
next: detachable WSF_FILTER_HANDLER [C]
|
||||
-- Next filter
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_next (a_next: WSF_FILTER_HANDLER [C])
|
||||
-- Set `next' to `a_next'
|
||||
do
|
||||
next := a_next
|
||||
ensure
|
||||
next_set: next = a_next
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
execute_next (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute the `next' filter.
|
||||
do
|
||||
if attached next as n then
|
||||
n.execute (ctx, req, res)
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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
|
||||
75
library/server/wsf/src/filter/wsf_routing_filter.e
Normal file
75
library/server/wsf/src/filter/wsf_routing_filter.e
Normal file
@@ -0,0 +1,75 @@
|
||||
note
|
||||
description: "Routing filter."
|
||||
author: "Olivier Ligot"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
WSF_ROUTING_FILTER [H -> WSF_HANDLER [C], C -> WSF_HANDLER_CONTEXT]
|
||||
|
||||
inherit
|
||||
WSF_FILTER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_router: WSF_ROUTER [H, C])
|
||||
do
|
||||
router := a_router
|
||||
ensure
|
||||
router_set: router = a_router
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
router: WSF_ROUTER [H, C]
|
||||
-- Router
|
||||
|
||||
execute_default_action: detachable PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]]
|
||||
-- `execute_default' action
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_execute_default_action (an_action: like execute_default_action)
|
||||
-- Set `execute_default_action' to `an_action'
|
||||
do
|
||||
execute_default_action := an_action
|
||||
ensure
|
||||
execute_default_action_set: execute_default_action = an_action
|
||||
end
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute the filter
|
||||
do
|
||||
if attached router.route (req) as r then
|
||||
router.execute_route (r, req, res)
|
||||
else
|
||||
execute_default (req, res)
|
||||
end
|
||||
execute_next (req, res)
|
||||
end
|
||||
|
||||
execute_default (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
if attached execute_default_action as action then
|
||||
action.call ([req, res])
|
||||
else
|
||||
do_nothing
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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
|
||||
@@ -0,0 +1,48 @@
|
||||
note
|
||||
description: "Summary description for {WSF_URI_TEMPLATE_FILTERED_SERVICE}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
WSF_URI_TEMPLATE_FILTERED_SERVICE
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize_filter
|
||||
-- Initialize `filter'
|
||||
do
|
||||
create_filter
|
||||
setup_filter
|
||||
end
|
||||
|
||||
create_filter
|
||||
-- Create `filter'
|
||||
deferred
|
||||
ensure
|
||||
filter_created: filter /= Void
|
||||
end
|
||||
|
||||
setup_filter
|
||||
-- Setup `filter'
|
||||
require
|
||||
filter_created: filter /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
filter: WSF_FILTER
|
||||
-- Filter
|
||||
|
||||
;note
|
||||
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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
|
||||
@@ -45,6 +45,11 @@ feature -- Status report
|
||||
is_string: BOOLEAN = False
|
||||
-- Is Current as a WSF_STRING representation?
|
||||
|
||||
is_empty: BOOLEAN
|
||||
do
|
||||
Result := value = Void
|
||||
end
|
||||
|
||||
feature -- Query
|
||||
|
||||
string_representation: STRING_32
|
||||
|
||||
@@ -97,6 +97,12 @@ feature -- Status report
|
||||
Result := values.count = 1
|
||||
end
|
||||
|
||||
is_empty: BOOLEAN
|
||||
-- Is Current empty?
|
||||
do
|
||||
Result := values.is_empty
|
||||
end
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
as_string: WSF_STRING
|
||||
|
||||
@@ -97,6 +97,12 @@ feature -- Status report
|
||||
end
|
||||
end
|
||||
|
||||
is_empty: BOOLEAN
|
||||
-- Is Current empty?
|
||||
do
|
||||
Result := values.is_empty
|
||||
end
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
as_string: WSF_STRING
|
||||
@@ -108,6 +114,59 @@ feature -- Conversion
|
||||
end
|
||||
end
|
||||
|
||||
as_array_of_string: detachable ARRAY [READABLE_STRING_32]
|
||||
-- Return an array of STRING if possible., otherwise Void
|
||||
local
|
||||
i,n: INTEGER
|
||||
nb: INTEGER
|
||||
do
|
||||
from
|
||||
i := 1
|
||||
n := count
|
||||
create Result.make_filled ("", 1, n)
|
||||
until
|
||||
i > n or Result = Void
|
||||
loop
|
||||
if attached {WSF_STRING} value (i.out) as s then
|
||||
Result.put (s.value, i)
|
||||
nb := nb + 1
|
||||
else
|
||||
Result := Void
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
if Result /= Void and then nb /= n then
|
||||
Result := Void
|
||||
end
|
||||
ensure
|
||||
is_array_of_string implies Result /= Void and then Result.count = count and then Result.lower = 1
|
||||
end
|
||||
|
||||
is_array_of_string: BOOLEAN
|
||||
-- Is Current representable as an array of string?
|
||||
local
|
||||
i,n, nb: INTEGER
|
||||
do
|
||||
from
|
||||
i := 1
|
||||
n := count
|
||||
nb := 0
|
||||
Result := True
|
||||
until
|
||||
i > n or not Result
|
||||
loop
|
||||
if attached {WSF_STRING} value (i.out) then
|
||||
nb := nb + 1
|
||||
else
|
||||
Result := False
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
if nb /= n then
|
||||
Result := False
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Element change
|
||||
|
||||
add_value (a_value: WSF_VALUE; k: READABLE_STRING_32)
|
||||
|
||||
@@ -44,7 +44,13 @@ feature -- Element change
|
||||
feature -- Status report
|
||||
|
||||
is_string: BOOLEAN = False
|
||||
-- Is Current as a WSF_STRING representation?
|
||||
-- Is Current as a WSF_STRING representation?
|
||||
|
||||
is_empty: BOOLEAN
|
||||
-- Is Current empty?
|
||||
do
|
||||
Result := size = 0
|
||||
end
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
|
||||
@@ -35,6 +35,12 @@ feature -- Status report
|
||||
deferred
|
||||
end
|
||||
|
||||
is_empty: BOOLEAN
|
||||
-- Is Current empty?
|
||||
--| i.e empty string, empty table, ...
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Query
|
||||
|
||||
as_string: WSF_STRING
|
||||
|
||||
@@ -42,18 +42,23 @@ feature {NONE} -- Initialization
|
||||
local
|
||||
h: like header
|
||||
do
|
||||
get_file_exists
|
||||
create h.make
|
||||
header := h
|
||||
|
||||
h.put_content_type (content_type)
|
||||
if attached file_last_modified as dt then
|
||||
h.put_last_modified (dt)
|
||||
end
|
||||
get_file_size
|
||||
if file_size = 0 then
|
||||
set_status_code ({HTTP_STATUS_CODE}.not_found)
|
||||
|
||||
if file_exists then
|
||||
if attached file_last_modified as dt then
|
||||
h.put_last_modified (dt)
|
||||
end
|
||||
get_file_size
|
||||
if file_size = 0 then
|
||||
set_status_code ({HTTP_STATUS_CODE}.not_found)
|
||||
else
|
||||
set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||
end
|
||||
else
|
||||
set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||
set_status_code ({HTTP_STATUS_CODE}.not_found)
|
||||
end
|
||||
update_content_length
|
||||
end
|
||||
@@ -62,12 +67,16 @@ feature {NONE} -- Initialization
|
||||
local
|
||||
n: INTEGER
|
||||
do
|
||||
n := file_size
|
||||
if attached head as h then
|
||||
n := n + h.count
|
||||
end
|
||||
if attached bottom as b then
|
||||
n := n + b.count
|
||||
if file_exists then
|
||||
n := file_size
|
||||
if attached head as h then
|
||||
n := n + h.count
|
||||
end
|
||||
if attached bottom as b then
|
||||
n := n + b.count
|
||||
end
|
||||
else
|
||||
n := 0
|
||||
end
|
||||
content_length := n
|
||||
header.put_content_length (n)
|
||||
@@ -75,9 +84,14 @@ feature {NONE} -- Initialization
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_expires (t: INTEGER)
|
||||
set_expires_in_seconds (sec: INTEGER)
|
||||
do
|
||||
header.put_expires (t)
|
||||
set_expires (sec.out)
|
||||
end
|
||||
|
||||
set_expires (s: STRING)
|
||||
do
|
||||
header.put_expires_string (s)
|
||||
end
|
||||
|
||||
set_no_cache
|
||||
@@ -104,6 +118,9 @@ feature -- Access
|
||||
|
||||
file_name: READABLE_STRING_8
|
||||
|
||||
file_exists: BOOLEAN
|
||||
-- File exists?
|
||||
|
||||
file_size: INTEGER
|
||||
-- Size of file named `file_name'
|
||||
|
||||
@@ -157,42 +174,54 @@ feature {WSF_RESPONSE} -- Output
|
||||
s: detachable READABLE_STRING_8
|
||||
do
|
||||
res.set_status_code (status_code)
|
||||
res.put_header_text (header.string)
|
||||
s := head
|
||||
if s /= Void then
|
||||
res.put_string (s)
|
||||
end
|
||||
if not answer_head_request_method then
|
||||
send_file_content_to (file_name, res)
|
||||
end
|
||||
s := bottom
|
||||
if s /= Void then
|
||||
res.put_string (s)
|
||||
if status_code = {HTTP_STATUS_CODE}.not_found then
|
||||
else
|
||||
res.put_header_text (header.string)
|
||||
s := head
|
||||
if s /= Void then
|
||||
res.put_string (s)
|
||||
end
|
||||
if not answer_head_request_method then
|
||||
send_file_content_to (file_name, res)
|
||||
end
|
||||
s := bottom
|
||||
if s /= Void then
|
||||
res.put_string (s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation: file system helper
|
||||
|
||||
get_file_size
|
||||
-- Get `file_size' from file named `file_name'
|
||||
get_file_exists
|
||||
-- Get `file_exists'
|
||||
local
|
||||
f: RAW_FILE
|
||||
do
|
||||
create f.make (file_name)
|
||||
if f.exists then
|
||||
file_size := f.count
|
||||
end
|
||||
file_exists := f.exists
|
||||
end
|
||||
|
||||
get_file_size
|
||||
-- Get `file_size' from file named `file_name'
|
||||
require
|
||||
file_exists: file_exists
|
||||
local
|
||||
f: RAW_FILE
|
||||
do
|
||||
create f.make (file_name)
|
||||
file_size := f.count
|
||||
end
|
||||
|
||||
file_last_modified: detachable DATE_TIME
|
||||
-- Get `file_size' from file named `file_name'
|
||||
require
|
||||
file_exists: file_exists
|
||||
local
|
||||
f: RAW_FILE
|
||||
do
|
||||
create f.make (file_name)
|
||||
if f.exists then
|
||||
create Result.make_from_epoch (f.change_date)
|
||||
end
|
||||
create Result.make_from_epoch (f.change_date)
|
||||
end
|
||||
|
||||
file_extension (fn: STRING): STRING
|
||||
@@ -231,11 +260,12 @@ feature {NONE} -- Implementation: output
|
||||
require
|
||||
string_not_empty: not fn.is_empty
|
||||
is_readable: (create {RAW_FILE}.make (fn)).is_readable
|
||||
file_exists: file_exists
|
||||
local
|
||||
f: RAW_FILE
|
||||
do
|
||||
create f.make (fn)
|
||||
check f.exists and then f.is_readable end
|
||||
check f.is_readable end
|
||||
|
||||
f.open_read
|
||||
from
|
||||
|
||||
@@ -177,6 +177,18 @@ feature -- Helper
|
||||
Result := request_method.is_case_insensitive_equal (m.as_string_8)
|
||||
end
|
||||
|
||||
is_post_request_method: BOOLEAN
|
||||
-- Is Current a POST request method?
|
||||
do
|
||||
Result := request_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_post)
|
||||
end
|
||||
|
||||
is_get_request_method: BOOLEAN
|
||||
-- Is Current a GET request method?
|
||||
do
|
||||
Result := request_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_get)
|
||||
end
|
||||
|
||||
feature -- Eiffel WGI access
|
||||
|
||||
wgi_version: READABLE_STRING_8
|
||||
@@ -259,6 +271,36 @@ feature -- Access: global variable
|
||||
end
|
||||
end
|
||||
|
||||
string_array_item (a_name: READABLE_STRING_GENERAL): detachable ARRAY [READABLE_STRING_32]
|
||||
-- Array of string values for path parameter `a_name' if relevant.
|
||||
do
|
||||
Result := string_array_item_for (a_name, agent item)
|
||||
end
|
||||
|
||||
string_array_item_for (a_name: READABLE_STRING_GENERAL; a_item_fct: FUNCTION [ANY, TUPLE [READABLE_STRING_GENERAL], detachable WSF_VALUE]): detachable ARRAY [READABLE_STRING_32]
|
||||
-- Array of string values for query parameter `a_name' if relevant.
|
||||
local
|
||||
i: INTEGER
|
||||
n: INTEGER
|
||||
do
|
||||
from
|
||||
i := 1
|
||||
n := 1
|
||||
create Result.make_filled ("", 1, 5)
|
||||
until
|
||||
i = 0
|
||||
loop
|
||||
if attached {WSF_STRING} a_item_fct.item ([a_name + "[" + i.out + "]"]) as v then
|
||||
Result.force (v.value, n)
|
||||
n := n + 1
|
||||
i := i + 1
|
||||
else
|
||||
i := 0 -- Exit
|
||||
end
|
||||
end
|
||||
Result.keep_head (n - 1)
|
||||
end
|
||||
|
||||
feature -- Execution variables
|
||||
|
||||
execution_variable (a_name: READABLE_STRING_GENERAL): detachable ANY
|
||||
@@ -1231,6 +1273,7 @@ feature {NONE} -- Implementation: smart parameter identification
|
||||
k.append_integer (tb.count + 1)
|
||||
end
|
||||
v := tb
|
||||
create n.make_from_string (n)
|
||||
n.append_character ('[')
|
||||
n.append (k)
|
||||
n.append_character (']')
|
||||
@@ -1280,6 +1323,9 @@ feature {NONE} -- Implementation: smart parameter identification
|
||||
--| Already done in previous part
|
||||
elseif attached {WSF_MULTIPLE_STRING} l_existing_value as l_multi then
|
||||
l_multi.add_value (v)
|
||||
elseif attached {WSF_TABLE} l_existing_value as l_table then
|
||||
-- Keep previous values (most likely we have table[1]=foo, table[2]=bar ..and table=/foo/bar
|
||||
-- Anyway for this case, we keep the previous version, and ignore this "conflict"
|
||||
else
|
||||
a_table.force (create {WSF_MULTIPLE_STRING}.make_with_array (<<l_existing_value, v>>), v.name)
|
||||
check replaced: a_table.found and then a_table.found_item ~ l_existing_value end
|
||||
|
||||
@@ -36,19 +36,25 @@ feature -- Encoder
|
||||
|
||||
encoded_string (s: READABLE_STRING_32): STRING_8
|
||||
-- HTML-encoded value of `s'.
|
||||
do
|
||||
Result := general_encoded_string (s)
|
||||
end
|
||||
|
||||
general_encoded_string (s: READABLE_STRING_GENERAL): STRING_8
|
||||
-- The HTML-encoded equivalent of the given string
|
||||
-- HTML-encoded value of `s'.
|
||||
local
|
||||
i, n: INTEGER
|
||||
uc: CHARACTER_32
|
||||
l_code: INTEGER
|
||||
l_code: NATURAL_32
|
||||
c: CHARACTER_8
|
||||
do
|
||||
has_error := False
|
||||
create Result.make (s.count + s.count // 10)
|
||||
n := s.count
|
||||
from i := 1 until i > n loop
|
||||
uc := s.item (i)
|
||||
if uc.is_character_8 then
|
||||
c := uc.to_character_8
|
||||
l_code := s.code (i)
|
||||
if l_code.is_valid_character_8_code then
|
||||
c := l_code.to_character_8
|
||||
|
||||
inspect c
|
||||
when '%T', '%N', '%R' then
|
||||
@@ -59,7 +65,6 @@ feature -- Encoder
|
||||
when '<' then Result.append_string ("<")
|
||||
when '>' then Result.append_string (">")
|
||||
else
|
||||
l_code := c.code
|
||||
if
|
||||
l_code <= 31 or -- Hexa 1F
|
||||
l_code = 127 -- Hexa 7F
|
||||
@@ -77,7 +82,7 @@ feature -- Encoder
|
||||
end
|
||||
else
|
||||
Result.append ("&#")
|
||||
Result.append (uc.code.out)
|
||||
Result.append (l_code.out)
|
||||
Result.extend (';')
|
||||
end
|
||||
i := i + 1
|
||||
|
||||
@@ -35,18 +35,24 @@ feature -- Encoder
|
||||
|
||||
encoded_string (s: READABLE_STRING_32): STRING_8
|
||||
-- URL-encoded value of `s'.
|
||||
do
|
||||
Result := general_encoded_string (s)
|
||||
end
|
||||
|
||||
general_encoded_string (s: READABLE_STRING_GENERAL): STRING_8
|
||||
-- URL-encoded value of `s'.
|
||||
local
|
||||
i, n: INTEGER
|
||||
uc: CHARACTER_32
|
||||
c: CHARACTER_8
|
||||
l_code: NATURAL_32
|
||||
do
|
||||
has_error := False
|
||||
create Result.make (s.count + s.count // 10)
|
||||
n := s.count
|
||||
from i := 1 until i > n loop
|
||||
uc := s.item (i)
|
||||
if uc.is_character_8 then
|
||||
c := uc.to_character_8
|
||||
l_code := s.code (i)
|
||||
if l_code.is_valid_character_8_code then
|
||||
c := l_code.to_character_8
|
||||
inspect c
|
||||
when
|
||||
'A' .. 'Z',
|
||||
@@ -55,20 +61,20 @@ feature -- Encoder
|
||||
then
|
||||
Result.extend (c)
|
||||
else
|
||||
Result.append (url_encoded_char (uc))
|
||||
Result.append (url_encoded_char (l_code))
|
||||
end
|
||||
else
|
||||
Result.append (url_encoded_char (uc))
|
||||
Result.append (url_encoded_char (l_code))
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
|
||||
partial_encoded_string (s: READABLE_STRING_32; a_ignore: ARRAY [CHARACTER]): READABLE_STRING_8
|
||||
partial_encoded_string (s: READABLE_STRING_GENERAL; a_ignore: ARRAY [CHARACTER]): STRING_8
|
||||
-- URL-encoded value of `s'.
|
||||
local
|
||||
i, n: INTEGER
|
||||
uc: CHARACTER_32
|
||||
l_code: NATURAL_32
|
||||
c: CHARACTER_8
|
||||
s8: STRING_8
|
||||
do
|
||||
@@ -77,9 +83,9 @@ feature -- Encoder
|
||||
Result := s8
|
||||
n := s.count
|
||||
from i := 1 until i > n loop
|
||||
uc := s.item (i)
|
||||
if uc.is_character_8 then
|
||||
c := uc.to_character_8
|
||||
l_code := s.code (i)
|
||||
if l_code.is_valid_character_8_code then
|
||||
c := l_code.to_character_8
|
||||
inspect c
|
||||
when
|
||||
'A' .. 'Z',
|
||||
@@ -91,14 +97,14 @@ feature -- Encoder
|
||||
if a_ignore.has (c) then
|
||||
s8.extend (c)
|
||||
else
|
||||
s8.append (url_encoded_char (uc))
|
||||
s8.append (url_encoded_char (l_code))
|
||||
end
|
||||
end
|
||||
else
|
||||
if a_ignore.has (c) then
|
||||
s8.extend (c)
|
||||
else
|
||||
s8.append (url_encoded_char (uc))
|
||||
s8.append (url_encoded_char (l_code))
|
||||
end
|
||||
end
|
||||
i := i + 1
|
||||
@@ -107,12 +113,12 @@ feature -- Encoder
|
||||
|
||||
feature {NONE} -- encoder character
|
||||
|
||||
url_encoded_char (uc: CHARACTER_32): STRING_8
|
||||
url_encoded_char (a_code: NATURAL_32): STRING_8
|
||||
do
|
||||
create Result.make (3)
|
||||
if uc.is_character_8 then
|
||||
if a_code.is_valid_character_8_code then
|
||||
Result.extend ('%%')
|
||||
Result.append (uc.code.to_hex_string)
|
||||
Result.append (a_code.to_hex_string)
|
||||
from
|
||||
until
|
||||
Result.count < 2 or else Result[2] /= '0'
|
||||
|
||||
@@ -17,6 +17,7 @@ inherit
|
||||
redefine
|
||||
default_create,
|
||||
name,
|
||||
general_encoded_string,
|
||||
encoded_string, partial_encoded_string,
|
||||
decoded_string
|
||||
end
|
||||
@@ -49,7 +50,17 @@ feature -- Encoder
|
||||
Result := Precursor (Result)
|
||||
end
|
||||
|
||||
partial_encoded_string (s: READABLE_STRING_32; a_ignore: ARRAY [CHARACTER]): READABLE_STRING_8
|
||||
general_encoded_string (s: READABLE_STRING_GENERAL): STRING_8
|
||||
do
|
||||
if attached {READABLE_STRING_32} s as s32 then
|
||||
Result := utf32_to_utf8 (s32)
|
||||
else
|
||||
Result := s.as_string_8
|
||||
end
|
||||
Result := Precursor (Result)
|
||||
end
|
||||
|
||||
partial_encoded_string (s: READABLE_STRING_GENERAL; a_ignore: ARRAY [CHARACTER]): STRING_8
|
||||
-- URL-encoded value of `s'.
|
||||
do
|
||||
Result := Precursor (s, a_ignore)
|
||||
|
||||
@@ -24,7 +24,7 @@ create {URI_TEMPLATE}
|
||||
make_from_uri_template
|
||||
|
||||
convert
|
||||
make ({READABLE_STRING_8})
|
||||
make ({READABLE_STRING_8, STRING_8})
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
|
||||
4
tools/ise_wizard/custom/ewf.dsc
Normal file
4
tools/ise_wizard/custom/ewf.dsc
Normal file
@@ -0,0 +1,4 @@
|
||||
NAME="EWF: Eiffel Web Framework application, multi-platform, with EWF source code"
|
||||
DESCRIPTION="Create a web server application based on the cross-platform library EWF. Using the original source code of EWF (i.e: not the one in $ISE_LIBRARY)"
|
||||
LOCATION="ewf_custom"
|
||||
PLATFORM="all"
|
||||
21
tools/ise_wizard/custom/resources/template.ecf
Normal file
21
tools/ise_wizard/custom/resources/template.ecf
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-10-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-10-0 http://www.eiffel.com/developers/xml/configuration-1-10-0.xsd" name="${WIZ:TARGET_NAME}" uuid="${WIZ:UUID}">
|
||||
<target name="${WIZ:TARGET_NAME}">
|
||||
<root class="EWF_APPLICATION" feature="make_and_launch"/>
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<setting name="concurrency" value="${WIZ:CONCURRENCY}"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="default_${WIZ:EWF_CONNECTOR}" location="${WIZ:EWF_DIR}/library/server/wsf/default/${WIZ:EWF_CONNECTOR}-safe.ecf"/>
|
||||
<library name="encoder" location="${WIZ:EWF_DIR}/library/text/encoder/encoder-safe.ecf" readonly="false"/>
|
||||
<library name="wsf" location="${WIZ:EWF_DIR}/library/server/wsf/wsf-safe.ecf" readonly="false"/>
|
||||
<library name="http" location="${WIZ:EWF_DIR}/library/network/protocol/http/http-safe.ecf" readonly="false"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -1,4 +1,4 @@
|
||||
NAME="Eiffel Web Framework application, multi-platform, with EWF"
|
||||
NAME="EWF: Eiffel Web Framework application, multi-platform, with EWF"
|
||||
DESCRIPTION="Create a web server application based on the cross-platform library EWF."
|
||||
LOCATION="ewf"
|
||||
PLATFORM="all"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-10-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-10-0 http://www.eiffel.com/developers/xml/configuration-1-10-0.xsd" name="ewf_ise_wizard" uuid="F881A707-745E-4C6D-90D1-F820EE3B1470">
|
||||
<target name="ewf_ise_wizard">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-10-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-10-0 http://www.eiffel.com/developers/xml/configuration-1-10-0.xsd" name="wizard" uuid="F881A707-745E-4C6D-90D1-F820EE3B1470">
|
||||
<target name="wizard">
|
||||
<root class="EWF_WIZARD" feature="make"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
@@ -15,4 +15,7 @@
|
||||
<library name="vision2" location="$ISE_LIBRARY\library\vision2\vision2-safe.ecf"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
<target name="custom_wizard" extends="wizard">
|
||||
<root class="EWF_CUSTOM_WIZARD" feature="make"/>
|
||||
</target>
|
||||
</system>
|
||||
|
||||
@@ -2,10 +2,10 @@ setlocal
|
||||
|
||||
rd /q/s tmp
|
||||
mkdir tmp
|
||||
ecb -config ewf_ise_wizard-safe.ecf -finalize -c_compile -project_path tmp
|
||||
ecb -config ewf_ise_wizard-safe.ecf -target wizard -finalize -c_compile -project_path tmp
|
||||
mkdir spec
|
||||
mkdir spec\%ISE_PLATFORM%
|
||||
move tmp\EIFGENs\ewf_ise_wizard\F_code\ewf_ise_wizard.exe spec\%ISE_PLATFORM%\wizard.exe
|
||||
move tmp\EIFGENs\wizard\F_code\wizard.exe spec\%ISE_PLATFORM%\wizard.exe
|
||||
rd /q/s tmp
|
||||
|
||||
set WIZ_TARGET=%ISE_EIFFEL%\studio\wizards\new_projects\ewf
|
||||
21
tools/ise_wizard/install_ise_wizard_custom.bat
Normal file
21
tools/ise_wizard/install_ise_wizard_custom.bat
Normal file
@@ -0,0 +1,21 @@
|
||||
setlocal
|
||||
|
||||
rd /q/s tmp
|
||||
mkdir tmp
|
||||
ecb -config ewf_ise_wizard-safe.ecf -target custom_wizard -finalize -c_compile -project_path tmp
|
||||
mkdir custom
|
||||
mkdir custom\spec
|
||||
mkdir custom\spec\%ISE_PLATFORM%
|
||||
move tmp\EIFGENs\custom_wizard\F_code\wizard.exe custom\spec\%ISE_PLATFORM%\wizard.exe
|
||||
rd /q/s tmp
|
||||
|
||||
set WIZ_TARGET=%ISE_EIFFEL%\studio\wizards\new_projects\ewf_custom
|
||||
rd /q/s %WIZ_TARGET%
|
||||
mkdir %WIZ_TARGET%
|
||||
xcopy /I /E /Y %~dp0\pixmaps %WIZ_TARGET%\pixmaps
|
||||
xcopy /I /E /Y %~dp0\resources %WIZ_TARGET%\resources
|
||||
copy %~dp0\custom\resources\* %WIZ_TARGET%\resources
|
||||
xcopy /I /E /Y %~dp0\custom\spec %WIZ_TARGET%\spec
|
||||
copy %~dp0custom\ewf.dsc %WIZ_TARGET%\..\ewf_custom.dsc
|
||||
|
||||
endlocal
|
||||
55
tools/ise_wizard/src/ewf_custom_wizard.e
Normal file
55
tools/ise_wizard/src/ewf_custom_wizard.e
Normal file
@@ -0,0 +1,55 @@
|
||||
note
|
||||
description : "Objects that ..."
|
||||
author : "$Author$"
|
||||
date : "$Date$"
|
||||
revision : "$Revision$"
|
||||
|
||||
class
|
||||
EWF_CUSTOM_WIZARD
|
||||
|
||||
inherit
|
||||
EWF_WIZARD
|
||||
redefine
|
||||
get_information,
|
||||
generate_project
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Form
|
||||
|
||||
get_information
|
||||
do
|
||||
if attached string_question ("Location of EWF source code (by default $EWF_DIR)?", Void, Void, False) as pn then
|
||||
ewf_dir := pn.string
|
||||
else
|
||||
ewf_dir := "$EWF_DIR"
|
||||
end
|
||||
Precursor
|
||||
end
|
||||
|
||||
feature -- Generation
|
||||
|
||||
generate_project (a_layout: WIZARD_LAYOUT)
|
||||
do
|
||||
if attached ewf_dir as d then
|
||||
variables.force (d, "EWF_DIR")
|
||||
Precursor (a_layout)
|
||||
else
|
||||
die (-1)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
ewf_dir: detachable READABLE_STRING_8
|
||||
|
||||
feature -- Change
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
invariant
|
||||
-- invariant_clause: True
|
||||
|
||||
end
|
||||
@@ -25,8 +25,6 @@ feature {NONE} -- Initialization
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Status
|
||||
|
||||
feature -- Access
|
||||
|
||||
project_directory_name: detachable READABLE_STRING_8
|
||||
@@ -65,7 +63,7 @@ feature -- Form
|
||||
projet_name := "ewf"
|
||||
end
|
||||
|
||||
if boolean_question ("Do you want to use router (Y|n) ? ", <<["y", True], ["Y", True]>>, "Y") then
|
||||
if boolean_question ("Do you want to use WSF_ROUTER (Y|n) ? ", <<["y", True], ["Y", True]>>, "Y") then
|
||||
use_router := True
|
||||
router_type := "uri-template"
|
||||
else
|
||||
@@ -121,6 +119,13 @@ feature -- Form
|
||||
tfn.add_extension ("ecf")
|
||||
copy_resource_template ("template.ecf", tfn.string)
|
||||
|
||||
create tfn.make_from_string (dn.string)
|
||||
|
||||
tfn.set_file_name ("ewf")
|
||||
tfn.add_extension ("ini")
|
||||
copy_resource_template ("ewf.ini", tfn.string)
|
||||
|
||||
|
||||
create res.make (tfn.string, d.name)
|
||||
|
||||
create dn.make_from_string (pdn)
|
||||
@@ -131,20 +136,15 @@ feature -- Form
|
||||
d.recursive_create_dir
|
||||
end
|
||||
create tfn.make_from_string (dn.string)
|
||||
tfn.set_file_name ("ewb_application")
|
||||
tfn.set_file_name ("ewf_application")
|
||||
tfn.add_extension ("e")
|
||||
if attached router_type as rt then
|
||||
check rt.same_string ("uri-template") end
|
||||
copy_resource_template ("ewb_application-"+ rt +".e", tfn.string)
|
||||
copy_resource_template ("ewf_application-"+ rt +".e", tfn.string)
|
||||
else
|
||||
copy_resource_template ("ewb_application.e", tfn.string)
|
||||
copy_resource_template ("ewf_application.e", tfn.string)
|
||||
end
|
||||
|
||||
create tfn.make_from_string (dn.string)
|
||||
|
||||
tfn.set_file_name ("ewf")
|
||||
tfn.add_extension ("ini")
|
||||
copy_resource_template ("ewf.ini", tfn.string)
|
||||
|
||||
send_response (res)
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user