This version follows more closely the INI specification (https://en.wikipedia.org/wiki/INI_file) and adds some improvements to the API.
Spec changes:
* Allow `#` and `;` for comments
* No more sections nesting as by the spec (which actually change nothing see below)
API changes:
* Renaming `ConfigTree` -> `IniFile` (as it's no more a "tree")
* No more coupling with a file path, use utility methods `load_string`, `load_file`, `write_to` instead
* Ability to iterate keys, values, sections and section content
* Ability to create `IniSection` by hand
* `IniFile` and `IniSection` implements `Map[String, nullable String]`
The biggest change is that sub-sections are now flattened.
Before, with the following ini:
~~~ini
[section1]
key1=value1
[section1.section2]
key2=value2
~~~
You would get this hierarchy:
~~~
section1
| key1=value1
`- section2
`- key2=value2
~~~
Now you get:
~~~
section1
`- key1=value1
section1.section2
`- key2=value2
~~~
Two independent (unnested) sections, one called `section1` and the other called `section1.section2`.
This actually change nothing if the client was using the `[]` operator with the `.` notation as:
~~~
ini["section1.section2.key2"] # still returns `value2`
~~~
Documentation and specification from the README:
# `ini` - Read and write INI configuration files
[INI files](https://en.wikipedia.org/wiki/INI_file) are simple text files with
a basic structure composed of sections, properties and values used to store
configuration parameters.
Here's an example from the `package.ini` of this package:
~~~nit
import ini
var package_ini = """
[package]
name=ini
desc=Read and write INI configuration files.
[upstream]
git=https://github.com/nitlang/nit.git
git.directory=lib/ini/
"""
~~~
## Basic usage
`IniFile` is used to parse INI strings and access their content:
~~~nit
var ini = new IniFile.from_string(package_ini)
assert ini["package.name"] == "ini"
assert ini["upstream.git.directory"] == "lib/ini/"
assert ini["unknown.unknown"] == null
~~~
`IniFile` can also load INI configuration from a file:
~~~nit
package_ini.write_to_file("my_package.ini")
ini = new IniFile.from_file("my_package.ini")
assert ini["package.name"] == "ini"
assert ini["upstream.git.directory"] == "lib/ini/"
"my_package.ini".to_path.delete
~~~
INI content can be added or edited through the `IniFile` API then written to
a stream or a file.
~~~nit
ini["package.name"] = "new name"
ini["upstream.git.directory"] = "/dev/null"
ini["section.key"] = "value"
var stream = new StringWriter
ini.write_to(stream)
assert stream.to_s == """
[package]
name=new name
desc=Read and write INI configuration files.
[upstream]
git=https://github.com/nitlang/nit.git
git.directory=/dev/null
[section]
key=value
"""
~~~
## INI content
### Properties
Properties are the basic element of the INI format.
Every property correspond to a *key* associated to a *value* thanks to the equal (`=`) sign.
~~~nit
ini = new IniFile.from_string("""
key1=value1
key2=value2
""")
assert ini["key1"] == "value1"
assert ini["key2"] == "value2"
assert ini.length == 2
~~~
Accessing an unknown property returns `null`:
~~~nit
assert ini["unknown"] == null
~~~
Properties can be iterated over:
~~~nit
var i = 1
for key, value in ini do
assert key == "key{i}"
assert value == "value{i}"
i += 1
end
~~~
Property keys cannot contain the character `=`.
Values can contain any character.
Spaces are trimmed.
~~~nit
ini = new IniFile.from_string("""
prop=erty1=value1
property2 = value2
property3=value3 ; with semicolon
""")
assert ini[";property1"] == null
assert ini["prop=erty1"] == null
assert ini["prop"] == "erty1=value1"
assert ini["property2"] == "value2"
assert ini[" property2 "] == "value2"
assert ini["property3"] == "value3 ; with semicolon"
~~~
Both keys and values are case sensitive.
~~~nit
ini = new IniFile.from_string("""
Property1=value1
property2=Value2
""")
assert ini["property1"] == null
assert ini["Property1"] == "value1"
assert ini["property2"] != "value2"
assert ini["property2"] == "Value2"
~~~
### Sections
Properties may be grouped into arbitrary sections.
The section name appears on a line by itself between square brackets (`[` and `]`).
All keys after the section declaration are associated with that section.
The is no explicit "end of section" delimiter; sections end at the next section
declaration or the end of the file.
Sections cannot be nested.
~~~nit
var content = """
key1=value1
key2=value2
[section1]
key1=value3
key2=value4
[section2]
key1=value5
"""
ini = new IniFile.from_string(content)
assert ini["key1"] == "value1"
assert ini["unknown"] == null
assert ini["section1.key1"] == "value3"
assert ini["section1.unknown"] == null
assert ini["section2.key1"] == "value5"
~~~
Sections can be iterated over:
~~~nit
i = 1
for section in ini.sections do
assert section.name == "section{i}"
assert section["key1"].has_prefix("value")
i += 1
end
~~~
When iterating over a file properties, only properties at root are returned.
`flatten` can be used to iterate over all properties including the one from
sections.
~~~nit
assert ini.join(", ", ": ") == "key1: value1, key2: value2"
assert ini.flatten.join(", ", ": ") ==
"key1: value1, key2: value2, section1.key1: value3, section1.key2: value4, section2.key1: value5"
i = 0
for key, value in ini do
i += 1
assert key == "key{i}" and value == "value{i}"
end
assert i == 2
~~~
Sections name may contain any character including brackets (`[` and `]`).
Spaces are trimmed.
~~~nit
ini = new IniFile.from_string("""
[[section1]]
key=value1
[ section 2 ]
key=value2
[section1.section3]
key=value3
""")
assert ini.sections.length == 3
assert ini["[section1].key"] == "value1"
assert ini["section 2.key"] == "value2"
assert ini["section1.section3.key"] == "value3"
assert ini.sections.last.name == "section1.section3"
~~~
The dot `.` notation is used to create new sections with `[]=`.
Unknown sections will be created on the fly.
~~~nit
ini = new IniFile
ini["key"] = "value1"
ini["section1.key"] = "value2"
ini["section2.key"] = "value3"
stream = new StringWriter
ini.write_to(stream)
assert stream.to_s == """
key=value1
[section1]
key=value2
[section2]
key=value3
"""
~~~
Sections can also be created manually:
~~~nit
ini = new IniFile
ini["key"] = "value1"
var section = new IniSection("section1")
section["key"] = "value2"
ini.sections.add section
stream = new StringWriter
ini.write_to(stream)
assert stream.to_s == """
key=value1
[section1]
key=value2
"""
~~~
### Comments
Comments are indicated by semicolon (`;`) or a number sign (`#`) at the begining
of the line. Commented lines are ignored as well as empty lines.
~~~nit
ini = new IniFile.from_string("""
; This is a comment.
; property1=value1
# This is another comment.
# property2=value2
""")
assert ini.is_empty
~~~
### Unicode support
INI files support Unicode:
~~~nit
ini = new IniFile.from_string("""
property❤=héhé
""")
assert ini["property❤"] == "héhé"
~~~
Pull-Request: #2752
Reviewed-by: Jean Privat <jean@pryen.org>
artifacts:
paths:
- tests/errlist
- - tests/*.xml
+ - tests/*.xml*
when: always
reports:
junit: tests/*.xml
- git diff --name-only origin/master..HEAD -- "*.nit" "*.res" "README.*" | grep -v "^tests/" > list0.txt || true
- xargs nitls -pP < list0.txt > list.txt
- xargs nitunit < list.txt
+ - junit2html nitunit.xml
artifacts:
paths:
- nitunit.xml*
- xargs nitunit -v < list.txt| tee log.txt
- grep -e KO log.txt > status.txt || true
- tail -3 log.txt >> status.txt
+ - junit2html nitunit.xml
artifacts:
paths:
- nitunit.xml*
- xargs nitunit -v < list.txt| tee log.txt
- grep -e KO log.txt > status.txt || true
- tail -3 log.txt >> status.txt
+ - junit2html nitunit.xml
artifacts:
paths:
- nitunit.xml*
- src/version.nit
- src/nitc_0
+valgrind:
+ stage: more_test
+ dependencies:
+ - build_more_tools
+ script:
+ - mkdir -p valgrind.out
+ - nitc src/nitc.nit # To warm-up the cache
+ - src/valgrind.sh --callgrind-out-file=valgrind.out/nitc.nitc.out nitc src/nitc.nit -vv
+ - callgrind_annotate valgrind.out/nitc.nitc.out > valgrind.out/nitc.nitc.txt
+ - src/valgrind.sh --callgrind-out-file=valgrind.out/niti.niti.out nit -- src/nit.nit tests/base_simple3.nit -vv
+ - callgrind_annotate valgrind.out/niti.niti.out > valgrind.out/niti.niti.txt
+ artifacts:
+ paths:
+ - valgrind.out
+
build_doc:
stage: more_test
dependencies:
paths:
- nitdoc.out
+nitmetrics:
+ stage: more_test
+ dependencies:
+ - build_more_tools
+ script:
+ - nitmetrics --all --log --log-dir nitmetrics.out --dir nitmetrics.out --keep-going lib src
+ artifacts:
+ paths:
+ - nitmetrics.out
+
build_catalog:
stage: more_test
dependencies:
length += cln
end
+ redef fun has(c)
+ do
+ if not c isa Int then return false
+ return super(c&255)
+ end
+
# var b = new Bytes.empty
# b.append([104, 101, 108, 108, 111])
# assert b.to_s == "hello"
redef fun next_by(step) do real.next_by(step * self.step)
end
+# An iterator that lazyly cache the current item.
+#
+# This class can be used as an helper to build simple iterator with a single and simplier `next_item` method.
+# The only constraint is that `next_item` returns null on the last item, so `null` cannot be a valid element.
+abstract class CachedIterator[E: Object]
+ super Iterator[E]
+
+ # Get the next item if any.
+ # Returns null if there is no next item.
+ fun next_item: nullable E is abstract
+
+ # The last item effectively read.
+ # `null` if on start, after a next of if no more items are available.
+ protected var cache: nullable E = null
+
+ # The current item, if any.
+ # If not, the cache is effectively filled (with `next_item`).
+ # Return `null` iff there is no more elements.
+ protected fun current_item: nullable E
+ do
+ var cache = self.cache
+ if cache != null then return cache
+ cache = next_item
+ self.cache = cache
+ return cache
+ end
+
+ redef fun item do return current_item.as(not null)
+
+ redef fun is_ok do return current_item != null
+
+ redef fun next do
+ # If needed, fill the cache (an consume the current element)
+ current_item
+ # Empty the cache (so the next element will be read)
+ cache = null
+ end
+end
+
# A collection that contains only one item.
#
# Used to pass arguments by reference.
# Iterator returned by `Reader::each_line`.
# See the aforementioned method for details.
class LineIterator
- super Iterator[String]
+ super CachedIterator[String]
# The original stream
var stream: Reader
- redef fun is_ok
+ redef fun next_item
do
- var res = not stream.eof
- if not res and close_on_finish then stream.close
- return res
- end
-
- redef fun item
- do
- var line = self.line
- if line == null then
- line = stream.read_line
+ if stream.eof then
+ if close_on_finish then stream.close
+ return null
end
- self.line = line
- return line
- end
-
- # The last line read (cache)
- private var line: nullable String = null
-
- redef fun next
- do
- # force the read
- if line == null then item
- # drop the line
- line = null
+ return stream.read_line
end
# Close the stream when the stream is at the EOF.
# Verbosity level (the higher the more verbose)
fun verbose_level: Int do
var opt = opt_start.value
- if opt > 0 then return opt
+ if opt > 0 then
+ return info_level
+ end
var v = ini["loader.verbose"]
- if v != null then return v.to_i
- return 4
+ if v != null and v.to_i > 0 then
+ return info_level
+ end
+ return warn_level
end
# Logger used to print things
- var logger: ConsoleLog is lazy do
- var logger = new ConsoleLog
+ var logger: PopLogger is lazy do
+ var logger = new PopLogger
logger.level = verbose_level
return logger
end
end
# Logger shortcut
- fun log: ConsoleLog do return config.logger
+ fun log: PopLogger do return config.logger
# Display a error and exit
fun error(msg: String) do
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# A simple logger for Nit
+#
+# ## Basic Usage
+#
+# Create a new `Logger` with a severity level threshold set to `warn_level`:
+#
+# ~~~
+# var logger = new Logger(warn_level)
+# ~~~
+#
+# Messages with a severity equal or higher than `warn_level` will be displayed:
+#
+# ~~~
+# logger.error "Displays an error."
+# logger.warn "Displays a warning."
+# ~~~
+#
+# Messages with a lower severity are silenced:
+#
+# ~~~
+# logger.info "Displays nothing."
+# ~~~
+#
+# `FileLogger` can be used to output the messages into a file:
+#
+# ~~~
+# var log_file = "my.log"
+#
+# logger = new FileLogger(warn_level, log_file, append = false)
+# logger.error("An error")
+# logger.info("Some info")
+# logger.close
+#
+# assert log_file.to_path.read_all == "An error\n"
+# log_file.to_path.delete
+# ~~~
+#
+# ## Severity levels
+#
+# Each message is associated with a level that indicate its severity.
+# Only messages with a severity equal to or higher than the logger `level`
+# threshold will be displayed.
+#
+# Severity levels from the most severe to the least severe:
+#
+# * `unknown_level`: An unknown message that should always be outputted.
+# * `fatal_level`: An unhandleable error that results in a program crash.
+# * `error_level`: A handleable error condition.
+# * `warn_level`: A warning.
+# * `info_level`: Generic (useful) information about system operation.
+# * `debug_level`: Low-level information for developpers.
+#
+# ## Formatting messages
+#
+# You can create custom formatters by implementing the `Formatter` interface.
+#
+# ~~~
+# class MyFormatter
+# super Formatter
+#
+# redef fun format(level, message) do
+# if level < warn_level then return super
+# return "!!!{message}!!!"
+# end
+# end
+# ~~~
+#
+# See `DefaultFormatter` for a more advanced implementation example.
+#
+# Each Logger can be given a default formatter used to format the every messages
+# before outputting them:
+#
+# ~~~
+# var formatter = new MyFormatter
+# var stderr = new StringWriter
+# var logger = new Logger(warn_level, stderr, formatter)
+#
+# logger.warn("This is a warning.")
+# assert stderr.to_s.trim.split("\n").last == "!!!This is a warning.!!!"
+# ~~~
+#
+# Optionally, a `Formatter` can be given to replace the `default_formatter`
+# used by default:
+#
+# ~~~
+# # Create a formatter with no default decorator
+# logger = new Logger(warn_level, stderr, null)
+#
+# # Display a message without any formatter
+# logger.warn("This is a warning.")
+# assert stderr.to_s.trim.split("\n").last == "This is a warning."
+#
+# # Display a message with a custom formatter
+# logger.warn("This is a warning.", formatter)
+# assert stderr.to_s.trim.split("\n").last == "!!!This is a warning.!!!"
+# ~~~
+module logger
+
+import console
+
+# A simple logging utility
+#
+# `Logger` provides a simple way to output messages from applications.
+#
+# Each message is associated with a level that indicate its severity.
+# Only messages with a severity equal to or higher than the logger `level`
+# threshold will be displayed.
+#
+# ~~~
+# var logger = new Logger(warn_level)
+# assert logger.unknown("unkown")
+# assert logger.fatal("fatal")
+# assert logger.error("error")
+# assert logger.warn("warn")
+# assert not logger.info("info")
+# assert not logger.debug("debug")
+# ~~~
+class Logger
+
+ # Severity threshold
+ #
+ # Messages with a severity level greater than or equal to `level` will be displayed.
+ # Default is `warn_level`.
+ #
+ # See `unknown_level`, `fatal_level`, error_level``, `warn_level`,
+ # `info_level` and `debug_level`.
+ var level: Int = warn_level is optional, writable
+
+ # Kind of `Writer` used to output messages
+ type OUT: Writer
+
+ # Writer used to output messages
+ #
+ # Default is `stderr`.
+ var out: OUT = stderr is optional
+
+ # Formatter used to format messages before outputting them
+ #
+ # By default no formatter is used.
+ #
+ # See `DefaultFormatter`.
+ var default_formatter: nullable Formatter = null is optional, writable
+
+ # Output a message with `level` severity
+ #
+ # Only output messages with `level` severity greater than of equal to `self.level`.
+ #
+ # ~~~
+ # var stderr = new StringWriter
+ # var logger = new Logger(warn_level, stderr, null)
+ #
+ # # This message will be displayed:
+ # assert logger.warn("This is a warning.")
+ # assert stderr.to_s.trim.split("\n").last == "This is a warning."
+ #
+ # # This message will not:
+ # assert not logger.info("This is some info.")
+ # assert stderr.to_s.trim.split("\n").last == "This is a warning."
+ # ~~~
+ #
+ # Each logger can be given a default formatter used to format the messages
+ # before outputting them:
+ #
+ # ~~~
+ # var formatter = new DefaultFormatter(no_color = true)
+ # logger = new Logger(warn_level, stderr, formatter)
+ # logger.warn("This is a warning.")
+ # assert stderr.to_s.trim.split("\n").last == "Warning: This is a warning."
+ # ~~~
+ #
+ # Optionally, a `Formatter` can be given to replace the `default_formatter`
+ # used by default.
+ #
+ # ~~~
+ # # Create a formatter with no default decorator
+ # logger = new Logger(warn_level, stderr, null)
+ #
+ # # Display a message without any formatter
+ # logger.warn("This is a warning.")
+ # assert stderr.to_s.trim.split("\n").last == "This is a warning."
+ #
+ # # Display a message with a custom formatter
+ # logger.warn("This is a warning.", formatter)
+ # assert stderr.to_s.trim.split("\n").last == "Warning: This is a warning."
+ # ~~~
+ fun add(level: Int, message: Writable, formatter: nullable Formatter): Bool do
+ var format = formatter or else default_formatter
+ if format == null then
+ return add_raw(level, message)
+ end
+ return add_raw(level, format.format(level, message))
+ end
+
+ # Output a message with `level` severity without formatting it
+ #
+ # Only output messages with `level` severity greater than of equal to `self.level`.
+ #
+ # ~~~
+ # var stderr = new StringWriter
+ # var logger = new Logger(warn_level, stderr, null)
+ #
+ # # This message will be displayed:
+ # assert logger.add_raw(warn_level, "This is a warning.")
+ # assert stderr.to_s.trim.split("\n").last == "This is a warning."
+ #
+ # # This message will not:
+ # assert not logger.add_raw(info_level, "This is some info.")
+ # assert stderr.to_s.trim.split("\n").last == "This is a warning."
+ # ~~~
+ fun add_raw(level: Int, message: Writable): Bool do
+ if level < self.level then return false
+ out.write(message.write_to_string)
+ out.write("\n")
+ return true
+ end
+
+ # Output a message with `unknown_level` severity
+ #
+ # Unkown severity messages are always outputted.
+ fun unknown(message: String, formatter: nullable Formatter): Bool do
+ return add(unknown_level, message, formatter)
+ end
+
+ # Output a message with `fatal_level` severity
+ fun fatal(message: String, formatter: nullable Formatter): Bool do
+ return add(fatal_level, message, formatter)
+ end
+
+ # Output a message with `error_level` severity
+ fun error(message: String, formatter: nullable Formatter): Bool do
+ return add(error_level, message, formatter)
+ end
+
+ # Output a message with `warn_level` severity
+ fun warn(message: String, formatter: nullable Formatter): Bool do
+ return add(warn_level, message, formatter)
+ end
+
+ # Output a message with `info_level` severity
+ fun info(message: String, formatter: nullable Formatter): Bool do
+ return add(info_level, message, formatter)
+ end
+
+ # Output a message with `debug` severity
+ fun debug(message: String, formatter: nullable Formatter): Bool do
+ return add(debug_level, message, formatter)
+ end
+end
+
+# Log messages to a file
+#
+# ~~~
+# var log_file = "my_file.log"
+# var logger = new FileLogger(warn_level, log_file, append = false)
+# logger.error("An error")
+# logger.info("Some info")
+# logger.close
+# assert log_file.to_path.read_all == "An error\n"
+#
+# logger = new FileLogger(warn_level, log_file, append = true)
+# logger.error("Another error")
+# logger.close
+# assert log_file.to_path.read_all == "An error\nAnother error\n"
+#
+# log_file.to_path.delete
+# ~~~
+class FileLogger
+ super Logger
+ autoinit level, file, append, default_formatter
+
+ redef type OUT: FileWriter
+
+ # File where messages will be written
+ var file: String
+
+ # Append messages to `file`
+ #
+ # If `append` is `false`, the `file` will be overwritten.
+ var append: Bool = true is optional
+
+ init do
+ var old = null
+ if append then
+ old = file.to_path.read_all
+ end
+ out = new FileWriter.open(file)
+ out.set_buffering_mode(0, buffer_mode_line)
+ if old != null then
+ out.write(old)
+ end
+ end
+
+ # Close the logger and its `file`
+ fun close do out.close
+end
+
+# Format messages before outputing them
+#
+# A `Logger` can use a `Formatter` to format the messages before outputting them.
+#
+# See `DefaultFormatter`.
+interface Formatter
+
+ # Format `message` depending of its severity `level`
+ fun format(level: Int, message: Writable): Writable do return message
+end
+
+# Default `Logger` formatter
+#
+# The default formatter decorates the messages with severity labels and colors.
+class DefaultFormatter
+ super Formatter
+
+ # Do not decorate messages with colors
+ #
+ # ~~~
+ # var formatter = new DefaultFormatter(no_color = true)
+ # assert formatter.format(error_level, "My message.") == "Error: My message."
+ # ~~~
+ var no_color = false is optional, writable
+
+ redef fun format(level, message) do
+ var string = message.write_to_string
+
+ if level == fatal_level then
+ string = "Fatal: {string}"
+ else if level == error_level then
+ string = "Error: {string}"
+ else if level == warn_level then
+ string = "Warning: {string}"
+ else if level == info_level then
+ string = "Info: {string}"
+ else if level == debug_level then
+ string = "Debug: {string}"
+ end
+
+ if no_color then return string
+
+ if level == fatal_level then
+ return string.red
+ else if level == error_level then
+ return string.red
+ else if level == warn_level then
+ return string.yellow
+ else if level == info_level then
+ return string.purple
+ else if level == debug_level then
+ return string.blue
+ end
+
+ return string
+ end
+end
+
+redef class Sys
+
+ # Unknown severity level
+ #
+ # These messages are always displayed.
+ #
+ # See `Logger`.
+ var unknown_level = 5
+
+ # Fatal severity level
+ #
+ # See `Logger`.
+ var fatal_level = 4
+
+ # Error severity level
+ #
+ # See `Logger`.
+ var error_level = 3
+
+ # Warning severity level
+ #
+ # See `Logger`.
+ var warn_level = 2
+
+ # Info severity level
+ #
+ # See `Logger`.
+ var info_level = 1
+
+ # Debug severity level
+ #
+ # See `Logger`.
+ var debug_level = 0
+end
--- /dev/null
+[package]
+name=logger
+tags=logging,lib
+maintainer=Alexandre Terrasa <alexandre@moz-code.org>
+license=Apache-2.0
+desc=A simple logger for Nit
+[upstream]
+browse=https://github.com/nitlang/nit/tree/master/lib/logger/
+git=https://github.com/nitlang/nit.git
+git.directory=lib/logger/
+homepage=http://nitlanguage.org
+issues=https://github.com/nitlang/nit/issues
Next, we’ll create a middleware handler called “LogHandler” that prints the requested
uri, the response status and the time it took to Popcorn to process the request.
-This example gives a simplified version of the `RequestClock` and `ConsoleLog` middlewares.
+This example gives a simplified version of the `RequestClock` and `PopLogger` middlewares.
~~~
import popcorn
be used to develop your app faster.
* `RequestClock`: initializes requests clock.
-* `ConsoleLog`: displays resquest and response status in console (can be used with `RequestClock`).
+* `PopLogger`: displays resquest and response status in console (can be used with `RequestClock`).
* `SessionInit`: initializes requests session (see the `Sessions` section).
* `StaticServer`: serves static files (see the `Serving static files with Popcorn` section).
* `Router`: a mountable mini-app (see the `Mountable routers` section).
module pop_logging
import pop_handlers
-import console
+import logger
import realtime
# Initialize a clock for the resquest.
end
# Display log info about request processing.
-class ConsoleLog
+class PopLogger
+ super Logger
super Handler
- # Logger level
- #
- # * `0`: silent
- # * `1`: errors
- # * `2`: warnings
- # * `3`: info
- # * `4`: debug
- #
- # Request status are always logged, whatever the logger level is.
- var level = 4 is writable
-
# Do we want colors in the console output?
- var no_colors = false
+ var no_color = false is optional
+
+ redef var default_formatter = new PopFormatter(no_color) is optional
redef fun all(req, res) do
var clock = req.clock
if clock != null then
- log "{req.method} {req.url} {status(res)} ({clock.total}s)"
+ add_raw(info_level, "{req.method} {req.url} {status(res)} ({clock.total}s)")
else
- log "{req.method} {req.url} {status(res)}"
+ add_raw(info_level, "{req.method} {req.url} {status(res)}")
end
end
# Colorize the request status.
private fun status(res: HttpResponse): String do
- if no_colors then return res.status_code.to_s
+ if no_color then return res.status_code.to_s
return res.color_status
end
+end
- # Display a `message` with `level`.
- #
- # Message will only be displayed if `level <= self.level`.
- # Colors will be used depending on `colors`.
- #
- # Use `0` for no coloration.
- private fun display(level: Int, message: String) do
- if level > self.level then return
- if no_colors then
- print message
- return
+class PopFormatter
+ super Formatter
+
+ # Do not decorate messages with colors
+ var no_color = false is optional, writable
+
+ redef fun format(level, message) do
+ var string = message.write_to_string
+
+ if level == fatal_level then
+ string = "[FATAL] {string}"
+ else if level == error_level then
+ string = "[ERROR] {string}"
+ else if level == warn_level then
+ string = "[WARN] {string}"
+ else if level == info_level then
+ string = "[INFO] {string}"
+ else if level == debug_level then
+ string = "[DEBUG] {string}"
end
- if level == 0 then print message
- if level == 1 then print message.red
- if level == 2 then print message.yellow
- if level == 3 then print message.blue
- if level == 4 then print message.gray
- end
-
- # Display a message wathever the `level`
- fun log(message: String) do display(0, message)
- # Display a red error `message`.
- fun error(message: String) do display(1, "[ERROR] {message}")
-
- # Display a yellow warning `message`.
- fun warning(message: String) do display(2, "[WARN] {message}")
-
- # Display a blue info `message`.
- fun info(message: String) do display(3, "[INFO] {message}")
+ if no_color then return string
+
+ if level == fatal_level then
+ return string.red
+ else if level == error_level then
+ return string.red
+ else if level == warn_level then
+ return string.yellow
+ else if level == info_level then
+ return string.blue
+ else if level == debug_level then
+ return string.gray
+ end
- # Display a gray debug `message`.
- fun debug(message: String) do display(4, "[DEBUG] {message}")
+ return string
+ end
end
+
redef class HttpRequest
# Time that request was received by the Popcorn app.
var clock: nullable Clock = null
import popcorn
import popcorn::pop_config
-import popcorn::pop_logging
import popcorn::pop_json
import popcorn::pop_repos
# Saves logs into a MongoDB collection
class PopTracker
- super ConsoleLog
super TrackerHandler
redef fun all(req, res) do
mutex.unlock
end
+ redef fun has(e)
+ do
+ mutex.lock
+ var result = real_collection.has(e)
+ mutex.unlock
+ return result
+ end
+
#
## The following method defs are conflict resolutions
#
graphviz \
libunwind-dev \
pkg-config \
+ libicu-dev \
# Get the code!
git \
ca-certificates \
end
# Try to load the file as a markdown document
var mdoc = modelbuilder.load_markdown(a)
- page.add modelbuilder.test_mdoc(mdoc)
+ var ts = modelbuilder.test_mdoc(mdoc)
+ if not ts.children.is_empty then page.add ts
end
for a in module_files do
var g = modelbuilder.identify_group(a)
if g == null then continue
- page.add modelbuilder.test_group(g)
+ var ts = modelbuilder.test_group(g)
+ if not ts.children.is_empty then page.add ts
end
for m in mmodules do
- page.add modelbuilder.test_markdown(m)
- var ts = modelbuilder.test_unit(m)
- if ts != null then page.add ts
+ var ts
+ ts = modelbuilder.test_markdown(m)
+ if not ts.children.is_empty then page.add ts
+ ts = modelbuilder.test_unit(m)
+ if ts != null and not ts.children.is_empty then page.add ts
end
var file = toolcontext.opt_output.value
app.use("/oauth", new GithubOAuthCallBack(config.github_client_id, config.github_client_secret))
app.use("/logout", new GithubLogout)
app.use("/*", new StaticHandler(toolcontext.share_dir / "nitweb", "index.html"))
- app.use_after("/*", new ConsoleLog)
+ app.use_after("/*", new PopLogger(info_level))
app.listen(config.app_host, config.app_port)
end
</system-out></testcase><testcase classname="nitunit.test_nitunit.X" name="foo1" time="0.0"><failure message="Syntax Error: unexpected operator '!'."></failure><system-out>assert !@#$%^&*()
</system-out></testcase><testcase classname="nitunit.test_nitunit.X" name="foo2" time="0.0"><system-err></system-err><system-out>var x = new X
assert x.foo2
-</system-out></testcase></testsuite><testsuite package="test_test_nitunit::test_test_nitunit"></testsuite><testsuite package="test_test_nitunit"><testcase classname="nitunit.test_test_nitunit.TestX" name="test_foo" time="0.0"><system-err></system-err></testcase><testcase classname="nitunit.test_test_nitunit.TestX" name="test_foo1" time="0.0"><error message="Runtime Error in file nitunit.out/gen_test_test_nitunit.nit">Runtime error: Assert failed (test_test_nitunit.nit:38)
+</system-out></testcase></testsuite><testsuite package="test_test_nitunit"><testcase classname="nitunit.test_test_nitunit.TestX" name="test_foo" time="0.0"><system-err></system-err></testcase><testcase classname="nitunit.test_test_nitunit.TestX" name="test_foo1" time="0.0"><error message="Runtime Error in file nitunit.out/gen_test_test_nitunit.nit">Runtime error: Assert failed (test_test_nitunit.nit:38)
</error></testcase><testcase classname="nitunit.test_test_nitunit.TestX" name="test_foo2" time="0.0"><system-err></system-err></testcase></testsuite></testsuites>
\ No newline at end of file
Docunits: Entities: 4; Documented ones: 0; With nitunits: 0
Test suites: Classes: 1; Test Cases: 2; Failures: 0
[SUCCESS] All 2 tests passed.
-<testsuites><testsuite package="test_nitunit5::test_nitunit5"></testsuite><testsuite package="test_nitunit5"><testcase classname="nitunit.test_nitunit5.TestNitunit5" name="test_path_is_set" time="0.0"><system-err></system-err></testcase><testcase classname="nitunit.test_nitunit5.TestNitunit5" name="test_path_is_suite_path" time="0.0"><system-err></system-err></testcase></testsuite></testsuites>
\ No newline at end of file
+<testsuites><testsuite package="test_nitunit5"><testcase classname="nitunit.test_nitunit5.TestNitunit5" name="test_path_is_set" time="0.0"><system-err></system-err></testcase><testcase classname="nitunit.test_nitunit5.TestNitunit5" name="test_path_is_suite_path" time="0.0"><system-err></system-err></testcase></testsuite></testsuites>
\ No newline at end of file
Test suites: Classes: 1; Test Cases: 3; Failures: 3
[FAILURE] 3/3 tests failed.
`nitunit.out` is not removed for investigation.
-<testsuites><testsuite package="test_nitunit6::test_nitunit6"></testsuite><testsuite package="test_nitunit6"><testcase classname="nitunit.test_nitunit6.TestNitunit6" name="test_foo" time="0.0"><failure message="Nitunit Error: before module test failed"></failure></testcase></testsuite></testsuites>
\ No newline at end of file
+<testsuites><testsuite package="test_nitunit6"><testcase classname="nitunit.test_nitunit6.TestNitunit6" name="test_foo" time="0.0"><failure message="Nitunit Error: before module test failed"></failure></testcase></testsuite></testsuites>
\ No newline at end of file
Test suites: Classes: 1; Test Cases: 3; Failures: 1
[FAILURE] 1/3 tests failed.
`nitunit.out` is not removed for investigation.
-<testsuites><testsuite package="test_nitunit7::test_nitunit7"></testsuite><testsuite package="test_nitunit7"><testcase classname="nitunit.test_nitunit7.TestNitunit7" name="test_foo" time="0.0"><system-err></system-err></testcase></testsuite></testsuites>
\ No newline at end of file
+<testsuites><testsuite package="test_nitunit7"><testcase classname="nitunit.test_nitunit7.TestNitunit7" name="test_foo" time="0.0"><system-err></system-err></testcase></testsuite></testsuites>
\ No newline at end of file
Test suites: Classes: 1; Test Cases: 3; Failures: 1
[FAILURE] 1/3 tests failed.
`nitunit.out` is not removed for investigation.
-<testsuites><testsuite package="test_nitunit8::test_nitunit8"></testsuite><testsuite package="test_nitunit8"><testcase classname="nitunit.test_nitunit8.TestNitunit8" name="test_foo" time="0.0"><system-err></system-err></testcase></testsuite></testsuites>
\ No newline at end of file
+<testsuites><testsuite package="test_nitunit8"><testcase classname="nitunit.test_nitunit8.TestNitunit8" name="test_foo" time="0.0"><system-err></system-err></testcase></testsuite></testsuites>
\ No newline at end of file
Test suites: Classes: 1; Test Cases: 7; Failures: 1
[FAILURE] 1/7 tests failed.
`nitunit.out` is not removed for investigation.
-<testsuites><testsuite package="test_nitunit11::test_nitunit11"></testsuite><testsuite package="test_nitunit11"><testcase classname="nitunit.test_nitunit11.TestNitunit11" name="test_baz" time="0.0"><system-err></system-err></testcase></testsuite></testsuites>
\ No newline at end of file
+<testsuites><testsuite package="test_nitunit11"><testcase classname="nitunit.test_nitunit11.TestNitunit11" name="test_baz" time="0.0"><system-err></system-err></testcase></testsuite></testsuites>
\ No newline at end of file
Test suites: Classes: 3; Test Cases: 8; Failures: 7
[FAILURE] 7/8 tests failed.
`nitunit.out` is not removed for investigation.
-<testsuites><testsuite package="test_nitunit4>"></testsuite><testsuite package="test_nitunit4::nitunit4"></testsuite><testsuite package="test_nitunit4::test_bad_comp"></testsuite><testsuite package="test_bad_comp"><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_good" time="0.0"><failure message="Compilation Error">test_nitunit4/test_bad_comp.nit:25,10--19: Error: method or variable `bad_method` unknown in `TestSuiteBadComp`.
+<testsuites><testsuite package="test_bad_comp"><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_good" time="0.0"><failure message="Compilation Error">test_nitunit4/test_bad_comp.nit:25,10--19: Error: method or variable `bad_method` unknown in `TestSuiteBadComp`.
</failure></testcase><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_bad" time="0.0"><failure message="Compilation Error">test_nitunit4/test_bad_comp.nit:25,10--19: Error: method or variable `bad_method` unknown in `TestSuiteBadComp`.
-</failure></testcase></testsuite><testsuite package="test_nitunit4::test_bad_comp2"></testsuite><testsuite package="test_bad_comp2"><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_good" time="0.0"><failure message="Compilation Error">nitunit.out/gen_test_bad_comp2.nit:11,10--17: Error: expected 1 argument(s) for `test_bad(param: Bool)`; got 0. See introduction at `test_nitunit4::TestSuiteBadComp::test_bad`.
+</failure></testcase></testsuite><testsuite package="test_bad_comp2"><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_good" time="0.0"><failure message="Compilation Error">nitunit.out/gen_test_bad_comp2.nit:11,10--17: Error: expected 1 argument(s) for `test_bad(param: Bool)`; got 0. See introduction at `test_nitunit4::TestSuiteBadComp::test_bad`.
</failure></testcase><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_bad" time="0.0"><failure message="Compilation Error">nitunit.out/gen_test_bad_comp2.nit:11,10--17: Error: expected 1 argument(s) for `test_bad(param: Bool)`; got 0. See introduction at `test_nitunit4::TestSuiteBadComp::test_bad`.
-</failure></testcase></testsuite><testsuite package="test_nitunit4::test_nitunit4"></testsuite><testsuite package="test_nitunit4"><testcase classname="nitunit.test_nitunit4.TestTestSuite" name="test_foo" time="0.0"><error message="Runtime Error in file nitunit.out/gen_test_nitunit4.nit">Before Test
+</failure></testcase></testsuite><testsuite package="test_nitunit4"><testcase classname="nitunit.test_nitunit4.TestTestSuite" name="test_foo" time="0.0"><error message="Runtime Error in file nitunit.out/gen_test_nitunit4.nit">Before Test
Tested method
After Test
Runtime assert: <TestTestSuite>.before
</error></testcase><testcase classname="nitunit.test_nitunit4.TestTestSuite" name="test_sav_conflict" time="0.0"><error message="Conflicting expected output: test_nitunit4/test_nitunit4.sav/test_sav_conflict.res, test_nitunit4/sav/test_sav_conflict.res and test_nitunit4/test_sav_conflict.res all exist">Before Test
Tested method
After Test
-</error></testcase></testsuite><testsuite package="test_nitunit4::test_nitunit4_base"></testsuite></testsuites>
\ No newline at end of file
+</error></testcase></testsuite></testsuites>
\ No newline at end of file
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+true
+#
+true
+# Copyright 2004-2008 Jean Privat <jean@pryen.org>
+true
+#
+true
+# Licensed under the Apache License, Version 2.0 (the "License");
+true
+# you may not use this file except in compliance with the License.
+true
+# You may obtain a copy of the License at
+true
+#
+true
+# http://www.apache.org/licenses/LICENSE-2.0
+true
+#
+true
+# Unless required by applicable law or agreed to in writing, software
+true
+# distributed under the License is distributed on an "AS IS" BASIS,
+true
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+true
+# See the License for the specific language governing permissions and
+true
+# limitations under the License.
+true
+
+true
+var f = new FileReader.open("test_file_read.nit")
+true
+var s: String
+true
+while not f.eof do
+true
+ s = f.read_line
+true
+ printn(s)
+true
+ printn("\n")
+true
+end
+true
+f.close
+true
+
+true
+f.reopen
+true
+printn(f.read(10))
+true
+printn("|")
+true
+printn(f.read_all)
+true
+---
+true
+# This file is part of NIT ( http://www.nitlanguage.org ).
+true
+# This file is part of NIT ( http://www.nitlanguage.org ).
+true
+true
+#
+true
+#
+true
+true
+# Copyright 2004-2008 Jean Privat <jean@pryen.org>
+true
+# Copyright 2004-2008 Jean Privat <jean@pryen.org>
+true
+true
+#
+true
+#
+true
+true
+# Licensed under the Apache License, Version 2.0 (the "License");
+true
+# Licensed under the Apache License, Version 2.0 (the "License");
+true
+true
+# you may not use this file except in compliance with the License.
+true
+# you may not use this file except in compliance with the License.
+true
+true
+# You may obtain a copy of the License at
+true
+# You may obtain a copy of the License at
+true
+true
+#
+true
+#
+true
+true
+# http://www.apache.org/licenses/LICENSE-2.0
+true
+# http://www.apache.org/licenses/LICENSE-2.0
+true
+true
+#
+true
+#
+true
+true
+# Unless required by applicable law or agreed to in writing, software
+true
+# Unless required by applicable law or agreed to in writing, software
+true
+true
+# distributed under the License is distributed on an "AS IS" BASIS,
+true
+# distributed under the License is distributed on an "AS IS" BASIS,
+true
+true
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+true
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+true
+true
+# See the License for the specific language governing permissions and
+true
+# See the License for the specific language governing permissions and
+true
+true
+# limitations under the License.
+true
+# limitations under the License.
+true
+true
+
+true
+
+true
+true
+var f = new FileReader.open("test_file_read.nit")
+true
+var f = new FileReader.open("test_file_read.nit")
+true
+true
+var s: String
+true
+var s: String
+true
+true
+while not f.eof do
+true
+while not f.eof do
+true
+true
+ s = f.read_line
+true
+ s = f.read_line
+true
+true
+ printn(s)
+true
+ printn(s)
+true
+true
+ printn("\n")
+true
+ printn("\n")
+true
+true
+end
+true
+end
+true
+true
+f.close
+true
+f.close
+true
+true
+
+true
+
+true
+true
+f.reopen
+true
+f.reopen
+true
+true
+printn(f.read(10))
+true
+printn(f.read(10))
+true
+true
+printn("|")
+true
+printn("|")
+true
+true
+printn(f.read_all)
+true
+printn(f.read_all)
+true
+---
+# This file is part of NIT ( http://www.nitlanguage.org ).
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+#
+# Copyright 2004-2008 Jean Privat <jean@pryen.org>
+# Copyright 2004-2008 Jean Privat <jean@pryen.org>
+#
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# You may obtain a copy of the License at
+#
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# Unless required by applicable law or agreed to in writing, software
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# limitations under the License.
+
+
+var f = new FileReader.open("test_file_read.nit")
+var f = new FileReader.open("test_file_read.nit")
+var s: String
+var s: String
+while not f.eof do
+while not f.eof do
+ s = f.read_line
+ s = f.read_line
+ printn(s)
+ printn(s)
+ printn("\n")
+ printn("\n")
+end
+end
+f.close
+f.close
+
+
+f.reopen
+f.reopen
+printn(f.read(10))
+printn(f.read(10))
+printn("|")
+printn("|")
+printn(f.read_all)
+printn(f.read_all)
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+var f
+var i
+var l = 0
+
+f = new FileReader.open("test_file_read.nit")
+i = f.each_line
+while i.is_ok do
+ l += 1
+ print i.item
+ print i.is_ok
+ i.next
+end
+f.close
+
+print "---"
+
+f = new FileReader.open("test_file_read.nit")
+i = f.each_line
+while i.is_ok do
+ print i.is_ok
+ print i.item
+ print i.is_ok
+ print i.item
+ print i.is_ok
+ i.next
+end
+f.close
+
+print "---"
+
+f = new FileReader.open("test_file_read.nit")
+i = f.each_line
+for x in [0..l[ do
+ print i.item
+ print i.item
+ i.next
+end
+f.close
echo >>$xml "</testsuite></testsuites>"
+if type junit2html >/dev/null; then
+ junit2html "$xml"
+fi
+
if [ -n "$nok" ]; then
exit 1
else