X-Git-Url: http://nitlanguage.org?ds=sidebyside diff --git a/src/web/web_base.nit b/src/web/web_base.nit index 9b29207..351d7a6 100644 --- a/src/web/web_base.nit +++ b/src/web/web_base.nit @@ -26,7 +26,7 @@ import popcorn::pop_repos class NitwebConfig super AppConfig - redef var default_db_name = "nitweb" + redef fun default_db_name do return "nitweb" # Model to use. var model: Model @@ -36,6 +36,19 @@ class NitwebConfig # Modelbuilder used to access sources. var modelbuilder: ModelBuilder + + # The JSON API does not filter anything by default. + # + # So we can cache the model view. + var view: ModelView is lazy do + var view = new ModelView(model) + view.min_visibility = private_visibility + view.include_fictive = true + view.include_empty_doc = true + view.include_attribute = true + view.include_test_suite = true + return view + end end # Specific handler for the nitweb API. @@ -51,19 +64,6 @@ abstract class APIHandler return model.mentity_by_full_name(full_name.from_percent_encoding) end - # The JSON API does not filter anything by default. - # - # So we can cache the model view. - var view: ModelView is lazy do - var view = new ModelView(config.model) - view.min_visibility = private_visibility - view.include_fictive = true - view.include_empty_doc = true - view.include_attribute = true - view.include_test_suite = true - return view - end - # Try to load the mentity from uri with `/:id`. # # Send 400 if `:id` is null. @@ -75,7 +75,7 @@ abstract class APIHandler 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.api_error(404, "MEntity `{id}` not found") end @@ -102,6 +102,16 @@ redef class HttpResponse fun api_error(status: Int, message: String) do json(new APIError(status, message), status) end + + # Write data as JSON and set the right content type header. + fun raw_json(json: nullable String, status: nullable Int) do + header["Content-Type"] = media_types["json"].as(not null) + if json == null then + send(null, status) + else + send(json, status) + end + end end # An error returned by the API. @@ -109,22 +119,60 @@ end # Can be serialized to json. class APIError super Jsonable + serialize # Reponse status var status: Int # Response error message var message: String +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 - # Json Object for this error - var json: JsonObject is lazy do - var obj = new JsonObject - obj["status"] = status - obj["message"] = message - return obj +# 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 core_serialize_to(v) do + v.serialize_attribute("web_url", mentity.web_url) + v.serialize_attribute("api_url", mentity.api_url) + v.serialize_attribute("name", mentity.name) end +end - redef fun to_json do return json.to_json +# A namespace token representation +# +# Used for namespace tokens like `::`, `>` and `$` +redef class String + super NSEntity end redef class MEntity @@ -135,58 +183,139 @@ redef class MEntity # 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["web_url"] = web_url - obj["api_url"] = api_url - return obj + redef fun core_serialize_to(v) do + super + v.serialize_attribute("namespace", namespace) + v.serialize_attribute("web_url", web_url) + v.serialize_attribute("api_url", api_url) end - # Get the full json repesentation of `self` with MEntityRefs resolved. - 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["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 - var modifiers = new JsonArray - for modifier in mentity.collect_modifiers do - modifiers.add modifier - end - obj["modifiers"] = modifiers + redef fun core_serialize_to(v) do + super + v.serialize_attribute("namespace", mentity.namespace) + v.serialize_attribute("web_url", mentity.web_url) + v.serialize_attribute("api_url", mentity.api_url) + v.serialize_attribute("name", mentity.name) + v.serialize_attribute("mdoc", mentity.mdoc_or_fallback) + v.serialize_attribute("visibility", mentity.visibility.to_s) + v.serialize_attribute("modifiers", mentity.collect_modifiers) var mentity = self.mentity if mentity isa MMethod then - obj["msignature"] = mentity.intro.msignature + v.serialize_attribute("msignature", mentity.intro.msignature) else if mentity isa MMethodDef then - obj["msignature"] = mentity.msignature + v.serialize_attribute("msignature", mentity.msignature) else if mentity isa MVirtualTypeProp then - obj["bound"] = to_mentity_ref(mentity.intro.bound) + v.serialize_attribute("bound", to_mentity_ref(mentity.intro.bound)) else if mentity isa MVirtualTypeDef then - obj["bound"] = to_mentity_ref(mentity.bound) + v.serialize_attribute("bound", to_mentity_ref(mentity.bound)) end - return obj - end - - redef fun full_json do - var obj = super - obj["location"] = mentity.location - return obj + v.serialize_attribute("location", mentity.location) end end redef class MDoc # Add doc down processing - redef fun json do - var obj = new JsonObject - obj["html_synopsis"] = html_synopsis.write_to_string - obj["html_documentation"] = html_documentation.write_to_string - return obj + redef fun core_serialize_to(v) do + v.serialize_attribute("html_synopsis", html_synopsis.write_to_string) + v.serialize_attribute("html_documentation", html_documentation.write_to_string) + end +end + +redef class MPackage + redef fun namespace do return new Namespace.from([to_ns_ref]) +end + +redef class MGroup + 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 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 + + 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 fun namespace do + return new Namespace.from([intro_mmodule.ns_for(visibility), "::", to_ns_ref: nullable NSEntity]) + end +end + +redef class MClassDef + 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 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 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 @@ -209,16 +338,11 @@ end redef class POSetElement[E] super Jsonable - # Return JSON representation of `self`. - fun json: JsonObject do + redef fun serialize_to(v) do assert self isa POSetElement[MEntity] - var obj = new JsonObject - obj["greaters"] = to_mentity_refs(greaters) - obj["direct_greaters"] = to_mentity_refs(direct_greaters) - obj["direct_smallers"] = to_mentity_refs(direct_smallers) - obj["smallers"] = to_mentity_refs(smallers) - return obj + v.serialize_attribute("greaters", to_mentity_refs(greaters)) + v.serialize_attribute("direct_greaters", to_mentity_refs(direct_greaters)) + v.serialize_attribute("direct_smallers", to_mentity_refs(direct_smallers)) + v.serialize_attribute("smallers", to_mentity_refs(smallers)) end - - redef fun to_json do return json.to_json end