import model::model_json
import doc_down
import popcorn
+import popcorn::pop_config
+import popcorn::pop_repos
-# Specific nitcorn Action that uses a Model
-class ModelHandler
- super Handler
+# Nitweb config file.
+class NitwebConfig
+ super AppConfig
+
+ redef var default_db_name = "nitweb"
# Model to use.
var model: Model
# MModule used to flatten model.
var mainmodule: MModule
- # Find the MEntity ` with `full_name`.
- fun find_mentity(model: ModelView, full_name: nullable String): nullable MEntity do
- if full_name == null then return null
- return model.mentity_by_full_name(full_name.from_percent_encoding)
- end
-
- # Init the model view from the `req` uri parameters.
- fun init_model_view(req: HttpRequest): ModelView do
- var view = new ModelView(model)
- var show_private = req.bool_arg("private") or else false
- if not show_private then view.min_visibility = protected_visibility
-
- view.include_fictive = req.bool_arg("fictive") or else false
- view.include_empty_doc = req.bool_arg("empty-doc") or else true
- view.include_test_suite = req.bool_arg("test-suite") or else false
- view.include_attribute = req.bool_arg("attributes") or else true
-
- return view
- end
-end
-
-# Specific handler for nitweb API.
-abstract class APIHandler
- super ModelHandler
+ # Modelbuilder used to access sources.
+ var modelbuilder: ModelBuilder
# The JSON API does not filter anything by default.
#
view.include_test_suite = true
return view
end
+end
+
+# Specific handler for the nitweb API.
+abstract class APIHandler
+ super Handler
+
+ # App config.
+ var config: NitwebConfig
+
+ # Find the MEntity ` with `full_name`.
+ fun find_mentity(model: ModelView, full_name: nullable String): nullable MEntity do
+ if full_name == null then return null
+ return model.mentity_by_full_name(full_name.from_percent_encoding)
+ end
# Try to load the mentity from uri with `/:id`.
#
fun mentity_from_uri(req: HttpRequest, res: HttpResponse): nullable MEntity do
var id = req.param("id")
if id == null then
- res.error 400
+ res.api_error(400, "Expected mentity full name")
return null
end
- var mentity = find_mentity(view, id)
+ var mentity = find_mentity(config.view, id)
if mentity == null then
- res.error 404
+ res.api_error(404, "MEntity `{id}` not found")
end
return mentity
end
end
+# A Rooter dedicated to APIHandlers.
+class APIRouter
+ super Router
+
+ # App config
+ var config: NitwebConfig
+end
+
+redef class HttpResponse
+
+ # Return an HTTP error response with `status`
+ #
+ # Like the rest of the API, errors are formated as JSON:
+ # ~~~json
+ # { "status": 404, "message": "Not found" }
+ # ~~~
+ fun api_error(status: Int, message: String) do
+ json(new APIError(status, message), status)
+ end
+end
+
+# An error returned by the API.
+#
+# Can be serialized to json.
+class APIError
+ super Jsonable
+
+ # Reponse status
+ var status: Int
+
+ # Response error message
+ var message: String
+
+ # Json Object for this error
+ var json: JsonObject is lazy do
+ var obj = new JsonObject
+ obj["status"] = status
+ obj["message"] = message
+ return obj
+ end
+
+ redef fun serialize_to(v) do json.serialize_to(v)
+end
+
+# Fullname representation that can be used to build decorated links
+#
+# * MPackage: `mpackage_name`
+# * MGroup: `(mpackage_name::)mgroup_name`
+class Namespace
+ super Array[nullable NSEntity]
+ super NSEntity
+ serialize
+
+ redef fun to_s do return self.join("")
+ redef fun serialize_to(v) do to_a.serialize_to(v)
+end
+
+# Something that goes in a Namespace
+#
+# Can be either:
+# * a `NSToken` for tokens like `::`, `>` and `$`
+# * a `MSRef` for references to mentities
+interface NSEntity
+ super Jsonable
+end
+
+# A reference to a MEntity that can be rendered as a link.
+#
+# We do not reuse `MEntityRef` ref since NSRef can be found in a ref and create
+# an infinite loop.
+class NSRef
+ super NSEntity
+ serialize
+
+ # The mentity to link to/
+ var mentity: MEntity
+
+ redef fun serialize_to(v) do
+ var obj = new JsonObject
+ obj["web_url"] = mentity.web_url
+ obj["api_url"] = mentity.api_url
+ obj["name"] = mentity.name
+ obj.serialize_to(v)
+ end
+end
+
+# A namespace token representation
+#
+# Used for namespace tokens like `::`, `>` and `$`
+redef class String
+ super NSEntity
+end
+
redef class MEntity
# URL to `self` within the web interface.
- fun web_url: String is abstract
+ fun web_url: String do return "/doc/" / full_name
# URL to `self` within the JSON api.
fun api_url: String do return "/api/entity/" / full_name
redef fun json do
var obj = super
+ obj["namespace"] = namespace
obj["web_url"] = web_url
obj["api_url"] = api_url
return obj
end
# Get the full json repesentation of `self` with MEntityRefs resolved.
- fun api_json(handler: ModelHandler): JsonObject do return json
+ fun api_json(handler: APIHandler): JsonObject do return full_json
+
+ # Return `self.full_name` as an object that can be serialized to json.
+ fun namespace: nullable Namespace do return null
+
+ # Return a new NSRef to `self`.
+ fun to_ns_ref: NSRef do return new NSRef(self)
end
redef class MEntityRef
redef fun json do
var obj = super
+ obj["namespace"] = mentity.namespace
obj["web_url"] = mentity.web_url
obj["api_url"] = mentity.api_url
obj["name"] = mentity.name
obj["mdoc"] = mentity.mdoc_or_fallback
obj["visibility"] = mentity.visibility
- obj["location"] = mentity.location
var modifiers = new JsonArray
for modifier in mentity.collect_modifiers do
modifiers.add modifier
end
return obj
end
+
+ redef fun full_json do
+ var obj = super
+ obj["location"] = mentity.location
+ return obj
+ end
end
redef class MDoc
# Add doc down processing
redef fun json do
- var obj = super
- obj["synopsis"] = synopsis
- obj["documentation"] = documentation
- obj["comment"] = comment
+ var obj = new JsonObject
obj["html_synopsis"] = html_synopsis.write_to_string
obj["html_documentation"] = html_documentation.write_to_string
- obj["html_comment"] = html_comment.write_to_string
return obj
end
end
redef class MPackage
- redef var web_url = "/package/{full_name}" is lazy
+ redef fun namespace do return new Namespace.from([to_ns_ref])
end
redef class MGroup
- redef var web_url = "/group/{full_name}" is lazy
+ redef fun namespace do
+ var p = parent
+ if p == null then
+ return new Namespace.from([to_ns_ref, ">": nullable NSEntity])
+ end
+ return new Namespace.from([p.namespace, to_ns_ref, ">": nullable NSEntity])
+ end
end
redef class MModule
- redef var web_url = "/module/{full_name}" is lazy
+ redef fun namespace do
+ var mgroup = self.mgroup
+ if mgroup == null then
+ return new Namespace.from([to_ns_ref])
+ end
+ return new Namespace.from([mgroup.mpackage.to_ns_ref, "::", to_ns_ref: nullable NSEntity])
+ end
- redef fun api_json(handler) do
- var obj = super
- obj["intro_mclassdefs"] = to_mentity_refs(collect_intro_mclassdefs(private_view))
- obj["redef_mclassdefs"] = to_mentity_refs(collect_redef_mclassdefs(private_view))
- obj["imports"] = to_mentity_refs(in_importation.direct_greaters)
- return obj
+ private fun ns_for(visibility: MVisibility): nullable Namespace do
+ if visibility <= private_visibility then return namespace
+ var mgroup = self.mgroup
+ if mgroup == null then return namespace
+ return mgroup.mpackage.namespace
end
end
redef class MClass
- redef var web_url = "/class/{full_name}" is lazy
-
- redef fun api_json(handler) do
- var obj = super
- obj["all_mproperties"] = to_mentity_refs(collect_accessible_mproperties(private_view))
- obj["intro_mproperties"] = to_mentity_refs(collect_intro_mproperties(private_view))
- obj["redef_mproperties"] = to_mentity_refs(collect_redef_mproperties(private_view))
- obj["parents"] = to_mentity_refs(collect_parents(private_view))
- return obj
+ redef fun namespace do
+ return new Namespace.from([intro_mmodule.ns_for(visibility), "::", to_ns_ref: nullable NSEntity])
end
end
redef class MClassDef
- redef var web_url = "/classdef/{full_name}" is lazy
-
- redef fun json do
- var obj = super
- obj["intro"] = to_mentity_ref(mclass.intro)
- obj["mpackage"] = to_mentity_ref(mmodule.mpackage)
- return obj
- end
-
- redef fun api_json(handler) do
- var obj = super
- obj["intro_mpropdefs"] = to_mentity_refs(collect_intro_mpropdefs(private_view))
- obj["redef_mpropdefs"] = to_mentity_refs(collect_redef_mpropdefs(private_view))
- return obj
+ redef fun namespace do
+ if is_intro then
+ return new Namespace.from([mmodule.ns_for(mclass.visibility), "$", to_ns_ref: nullable NSEntity])
+ else if mclass.intro_mmodule.mpackage != mmodule.mpackage then
+ return new Namespace.from([mmodule.namespace, "$", mclass.namespace: nullable NSEntity])
+ else if mclass.visibility > private_visibility then
+ return new Namespace.from([mmodule.namespace, "$", mclass.to_ns_ref: nullable NSEntity])
+ end
+ return new Namespace.from([mmodule.full_name, "$::", mclass.intro_mmodule.to_ns_ref: nullable NSEntity])
end
end
redef class MProperty
- redef var web_url = "/property/{full_name}" is lazy
-
- redef fun json do
- var obj = super
- obj["intro_mclass"] = to_mentity_ref(intro_mclassdef.mclass)
- obj["mpackage"] = to_mentity_ref(intro_mclassdef.mmodule.mpackage)
- return obj
+ redef fun namespace do
+ if intro_mclassdef.is_intro then
+ return new Namespace.from([intro_mclassdef.mmodule.ns_for(visibility), "::", intro_mclassdef.mclass.to_ns_ref, "::", to_ns_ref: nullable NSEntity])
+ else
+ return new Namespace.from([intro_mclassdef.mmodule.namespace, "::", intro_mclassdef.mclass.to_ns_ref, "::", to_ns_ref: nullable NSEntity])
+ end
end
end
redef class MPropDef
- redef var web_url = "/propdef/{full_name}" is lazy
-
- redef fun json do
- var obj = super
- obj["intro"] = to_mentity_ref(mproperty.intro)
- obj["intro_mclassdef"] = to_mentity_ref(mproperty.intro.mclassdef)
- obj["mmodule"] = to_mentity_ref(mclassdef.mmodule)
- obj["mgroup"] = to_mentity_ref(mclassdef.mmodule.mgroup)
- obj["mpackage"] = to_mentity_ref(mclassdef.mmodule.mpackage)
- return obj
+ redef fun namespace do
+ var res = new Namespace
+ res.add mclassdef.namespace
+ res.add "$"
+
+ if mclassdef.mclass == mproperty.intro_mclassdef.mclass then
+ res.add to_ns_ref
+ else
+ if mclassdef.mmodule.mpackage != mproperty.intro_mclassdef.mmodule.mpackage then
+ res.add mproperty.intro_mclassdef.mmodule.ns_for(mproperty.visibility)
+ res.add "::"
+ else if mproperty.visibility <= private_visibility then
+ if mclassdef.mmodule.namespace_for(mclassdef.mclass.visibility) != mproperty.intro_mclassdef.mmodule.mpackage then
+ res.add "::"
+ res.add mproperty.intro_mclassdef.mmodule.to_ns_ref
+ res.add "::"
+ end
+ end
+ if mclassdef.mclass != mproperty.intro_mclassdef.mclass then
+ res.add mproperty.intro_mclassdef.to_ns_ref
+ res.add "::"
+ end
+ res.add to_ns_ref
+ end
+ return res
end
end
return obj
end
- redef fun to_json do return json.to_json
+ redef fun serialize_to(v) do json.serialize_to(v)
end