# Get the web page
var body = download_html_page
+ if opts.verbose.value > 1 then
+ print " # Body"
+ print body
+ end
+
# Parse the Web page and get the available beers
var beers = parse_beers_from_html(body)
+ if opts.verbose.value > 0 then
+ print " # Beers"
+ print beers
+ end
+
var db = new DB.open(db_path)
# Update the database with the beers of the day
# Set the email if desired
if send_emails then
var subs = db.subscribers
+ if opts.verbose.value > 0 then
+ print " # Subscribers"
+ print subs
+ end
send_emails_to subs
end
var of_interest = body.substring(start, finish-start)
var lines = of_interest.strip_tags.to_clean_lines
+ if opts.verbose.value > 0 then
+ print " # Lines"
+ print lines
+ end
+
var beers = new HashSet[Beer]
for line in lines do
- var parts = line.split(" - ")
+ var parts = line.split("- ")
if parts.length >= 2 then
beers.add new Beer(parts[0].trim, parts[1].trim)
end
# Shall we mail the mailing list?
var send_emails = new OptionBool("Send emails to subscribers", "-e", "--email")
+ # Display more debug messages
+ var verbose = new OptionCount("Display extra debug messages", "-v")
+
# Print the usage message
var help = new OptionBool("Print this help message", "-h", "--help")
- redef init do add_option(send_emails, help)
+ redef init do add_option(send_emails, verbose, help)
+end
+
+redef class Sys
+ # Command line options
+ var opts = new OptionContext
end
# Avoid executing when running tests
if "NIT_TESTING".environ == "true" then exit 0
-var opts = new OptionContext
opts.parse args
if not opts.errors.is_empty or opts.help.value == true then
print opts.errors.join("\n")
mv javap* gen/
src/serial.nit: $(shell ../../bin/nitls -M src/jwrapper.nit)
- ../../bin/nitserial -o src/serial.nit -d package src/jwrapper.nit
+ ../../bin/nitserial -o src/serial.nit src/jwrapper.nit
bin/jwrapper: src/javap_test_parser.nit src/serial.nit $(shell ../../bin/nitls -M src/jwrapper.nit) ../../bin/nitc
mkdir -p bin
var identifier: String
# If this NitType was found in `lib/android`, contains the module name to import
- var mod: nullable NitModule
+ var mod: nullable NitModuleRef
# Is this type known, wrapped and available in Nit?
var is_known: Bool = true
var constructors = new Array[JavaConstructor]
# Importations from this class
- var imports = new HashSet[NitModule]
+ var imports = new HashSet[NitModuleRef]
# Interfaces implemented by this class
var implements = new HashSet[JavaType]
end
# A Nit module, use to import the referenced extern classes
-class NitModule
+class NitModuleRef
# Relative path to the module
var path: String
var name: String is lazy do return path.basename(".nit")
redef fun to_s do return self.name
- redef fun ==(other) do return other isa NitModule and self.path == other.path
+ redef fun ==(other) do return other isa NitModuleRef and self.path == other.path
redef fun hash do return self.path.hash
end
# * The value is the corresponding `NitType`.
var find_extern_class: DefaultMap[String, nullable NitType] is lazy do
var map = new DefaultMap[String, nullable NitType](null)
- var modules = new HashMap[String, NitModule]
+ var modules = new HashMap[String, NitModuleRef]
var lib_paths = opt_libs.value
if lib_paths == null then lib_paths = new Array[String]
var mod = modules.get_or_null(path)
if mod == null then
- mod = new NitModule(path)
+ mod = new NitModuleRef(path)
modules[path] = mod
end
wiki.name=MyWiki
wiki.desc=proudly powered by nit
wiki.logo=assets/logo.png
-wiki.root_dir=/full/path/to/your/wiki/root/dir
<header>
- <a href="#"><img src="%ROOT_URL%/%LOGO%" alt="logo"/></a>
+ <a href="%ROOT_URL%/index.html"><img src="%ROOT_URL%/%LOGO%" alt="logo"/></a>
<h2>%SUBTITLE%</h2>
<h1>%TITLE%</h1>
</header>
--- /dev/null
+first page
--- /dev/null
+# A independant trail
+
+a [[trail: first]] and a [[trail: second]] page.
--- /dev/null
+a second page
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
- <a class="navbar-brand" href="%ROOT_URL%index.html">%TITLE%</a>
+ <a class="navbar-brand" href="%ROOT_URL%/index.html">%TITLE%</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
fun load_template(template_file: String): TemplateString do
var tpl = wiki.load_template(template_file)
if tpl.has_macro("ROOT_URL") then
- var root_dir = href.dirname.relpath("")
- # Avoid issues if the macro is just followed by a `/` (as with url prefix)
- if root_dir == "" then root_dir = "."
- tpl.replace("ROOT_URL", root_dir)
+ tpl.replace("ROOT_URL", root_href)
end
return tpl
end
var res = new Template
res.add "<ul class=\"trail\">"
- if pos > 0 then
- var target = flat[pos-1]
- res.add "<li>{target.a_from(self, "prev")}</li>"
- end
var parent = wiki.trails.parent(self)
+ # Up and prev are disabled on a root
if parent != null then
+ if pos > 0 then
+ var target = flat[pos-1]
+ res.add "<li>{target.a_from(self, "prev")}</li>"
+ end
res.add "<li>{parent.a_from(self, "up")}</li>"
end
if pos < flat.length - 1 then
var target = flat[pos+1]
- res.add "<li>{target.a_from(self, "next")}</li>"
+ # Only print the next if it is not a root
+ if target.parent != null then
+ res.add "<li>{target.a_from(self, "next")}</li>"
+ end
end
res.add "</ul>"
# Relative path to `self` from the target root_url
fun href: String do return breadcrumbs.join("/")
+ # Relative path to the directory `self` from the target root_url
+ fun dir_href: String do return href.dirname
+
+ # Relative path to the root url from `self`
+ fun root_href: String do
+ var root_dir = dir_href.relpath("")
+ # Avoid issues if used as a macro just followed by a `/` (as with url prefix)
+ if root_dir == "" then root_dir = "."
+ return root_dir
+ end
+
# A relative `href` to `self` from the page `context`.
#
# Should be used to navigate between documents.
fun href_from(context: WikiEntry): String
do
- var res = context.href.dirname.relpath(href)
+ var res = context.dir_href.relpath(href)
return res
end
end
return new WikiSectionIndex(wiki, "index", self)
end
+
+ redef fun dir_href do return href
end
redef class WikiArticle
redef fun title do return section.title
redef fun href do return section.href
+
+ redef fun dir_href do return section.dir_href
end
# A MarkdownProcessor able to parse wiki links.
class Achievement
super GameEntity
- redef var key is lazy do return "achievements" / id
redef var game
+ redef var key is lazy do
+ var owner = self.owner
+ if owner == null then return id
+ return "{owner.key}-{id}"
+ end
+
# Uniq ID for this achievement.
var id: String
# Is this achievement unlocked by somebody?
var is_unlocked: Bool is lazy do return not load_events.is_empty
+ # Game entity this achievement is about.
+ var owner: nullable GameEntity = null
+
# Init `self` from a `json` object.
#
# Used to load achievements from storage.
json["name"] = name
json["desc"] = desc
json["reward"] = reward
+ json["game"] = game.key
+ if owner != null then json["owner"] = owner.key
return json
end
end
if not event.action == "closed" then return
if not event.pull.merged then return
var player = event.pull.user.player(game)
- if player.stats["commits"] == threshold then
+ if player.stats["commits"] >= threshold then
var a = new_achievement(game)
player.unlock_achievement(a, event)
end
class Player1KComments
super PlayerXComments
- redef var id = "player_1000__comments"
+ redef var id = "player_1000_comments"
redef var name = "You sir, talk a lot!"
redef var desc = "Comment 1000 times on issues."
redef var reward = 1000
redef class GameEntity
- # Saves `event` in `self`.
- fun add_event(event: GameEvent) do event.save_in(self.key)
+ fun add_event(event: GameEvent) do
+ event.owner = self
+ event.save
+ end
# List all events registered in this entity.
#
class GameEvent
super GameEntity
- redef var key is lazy do return "events" / internal_id
redef var game
+ # Entity this event belongs to.
+ var owner: nullable GameEntity = null
+
# String used to dissociate events in the display.
var kind: String
# GameEvent uniq id used for storage.
var internal_id: String is noinit
+ redef var key = internal_id is lazy
+
# Date and time of the event.
var time: ISODate is noinit, writable
json["kind"] = kind
json["time"] = time.to_s
json["data"] = data
+ json["game"] = game.key
+ if owner != null then json["owner"] = owner.key
return json
end
end
redef fun game do return self
- # Returns the repo `full_name`.
- #
- # Example: `"nitlang/nit"`
- redef fun key do return repo.full_name
-
# We need a `GithubAPI` client to load Github data.
var api: GithubAPI
# A game takes place in a `github::Repo`.
var repo: Repo
+ # Game name
+ var name: String = repo.full_name is lazy
+
# Directory where game data are stored.
var game_dir: String is lazy do return "nitrpg_data" / repo.full_name
+ redef var key = name is lazy
+
# Used for data storage.
#
# File are stored in `game_dir`.
# Used to load entities from saved data.
fun from_json(json: JsonObject) do end
+ redef fun to_json do
+ var json = super
+ json["name"] = name
+ return json
+ end
+
# Create a player from a Github `User`.
#
# Or return the existing one from game data.
class Player
super GameEntity
- # Key is based on player `name`.
- redef var key is lazy do return "players" / name
-
redef var game
# FIXME contructor should be private
# The name is also used to load the user data lazilly from Github API.
var name: String
+ redef var key = name is lazy
+
# Player amount of nitcoins.
#
# Nitcoins is the currency used in nitrpg.
redef fun to_json do
var json = super
+ json["game"] = game.key
json["name"] = name
json["nitcoins"] = nitcoins
return json
# Rewards player for opened pull requests.
redef fun react_player_event(r, game) do
- if action == "opened" then
+ if action == "opened" or action == "reopened" then
react_pull_open(r, game)
else if action == "closed" then
react_pull_close(r, game)
# Rewards player for review comments.
#
# TODO only give nitcoins if reviewers < 2
+ # TODO give more points to first reviewer
redef fun react_player_event(r, game) do
if comment.is_ack then
react_player_review(r, game)
end
end
+ # TODO same player should not be authorized to review multiple times? How to handle rerols?
private fun react_player_review(r: PlayerReactor, game: Game) do
+ if issue.state == "closed" then return
var player = comment.user.player(game)
+ if issue.user == player.user then return
player.nitcoins += r.nc_pull_review
player.save
var event = player_reward_event("pull_review", player, r.nc_pull_review)
# The GameEntity monitored by these statistics.
var owner: GameEntity
- redef var key = "stats"
+ # Current date to extract stats
+ private var date = new Tm.gmtime
# Returns the `GameStats` instance for the overall statistics.
- var overall: GameStats is lazy do
- return load_stats_for("all")
- end
+ var overall: GameStats = load_stats_for("all") is lazy
# Returns the `GameStats` instance for the current year statistics.
- var yearly: GameStats is lazy do
- var date = new Tm.gmtime
- var key = date.strftime("%Y")
- return load_stats_for(key)
- end
+ var yearly: GameStats = load_stats_for(date.strftime("%Y")) is lazy
# Returns the `GameStats` instance for the current month statistics.
- var monthly: GameStats is lazy do
- var date = new Tm.gmtime
- var key = date.strftime("%Y-%m")
- return load_stats_for(key)
- end
+ var monthly: GameStats = load_stats_for(date.strftime("%Y-%m")) is lazy
# Returns the `GameStats` instance for the current day statistics.
- var daily: GameStats is lazy do
- var date = new Tm.gmtime
- var key = date.strftime("%Y-%m-%d")
- return load_stats_for(key)
- end
+ var daily: GameStats = load_stats_for(date.strftime("%Y-%m-%d")) is lazy
# Returns the `GameStats` instance for the current week statistics.
- var weekly: GameStats is lazy do
- var date = new Tm.gmtime
- var key = date.strftime("%Y-W%U")
- return load_stats_for(key)
- end
+ var weekly: GameStats = load_stats_for(date.strftime("%Y-W%U")) is lazy
# Load statistics for a `period` key.
fun load_stats_for(period: String): GameStats do
var key = owner.key / self.key / period
if not game.store.has_key(key) then
- return new GameStats(game, period)
+ return new GameStats(game, period, owner)
end
var json = game.store.load_object(key)
- return new GameStats.from_json(game, period, json)
+ return new GameStats.from_json(game, period, owner, json)
end
redef fun [](key) do return overall[key]
redef var game
- # The pedriod these stats are about.
+ # The period these stats are about.
var period: String
- redef fun key do return period
+ # The game entity these stats are about.
+ var owner: GameEntity
+
+ redef var key = "{owner.key}-{period}" is lazy
# Load `self` from saved data.
- init from_json(game: Game, period: String, json: JsonObject) do
- for k, v in json do self[k] = v.as(Int)
+ init from_json(game: Game, period: String, owner: GameEntity, json: JsonObject) do
+ var values = json.get_or_null("values")
+ if not values isa JsonObject then return
+ for k, v in values do self[k] = v.as(Int)
end
redef fun to_json do
- var obj = new JsonObject
- for k, v in self do obj[k] = v
+ var obj = super
+ obj["period"] = period
+ obj["owner"] = owner.key
+ var values = new JsonObject
+ values.recover_with(self)
+ obj["values"] = values
return obj
end
redef fun url do return "{root_url}/games" / key
- # Displayed name.
- fun name: String do return repo.full_name
-
# Return a HTML link to this Game.
fun link: String do return "<a href=\"{url}\">{name}</a>"
end
var default_soundpool: SoundPool is lazy do return new SoundPool
# Get the native audio manager
- fun audio_manager: NativeAudioManager import native_activity in "Java" `{
+ private fun audio_manager: NativeAudioManager import native_activity in "Java" `{
return (AudioManager)App_native_activity(self).getSystemService(Context.AUDIO_SERVICE);
`}
# WARNING: SEE: `TermCharFormat`
fun underline: String do return apply_format(normal.underline)
end
+
+# A dynamic progressbar displayable in console.
+#
+# Example:
+# ~~~nitish
+# var max = 10
+# var current = 0
+# var pb = new TermProgress(max, current)
+#
+# pb.display
+# for i in [current + 1 .. max] do
+# nanosleep(1, 0)
+# pb.update(i)
+# end
+#
+# print "\ndone"
+# ~~~
+#
+# Progressbar can accept metadata to display a small amount of data.
+#
+# Example with metadata:
+# ~~~nitish
+# var pb = new TermProgress(10, 0)
+# for i in [0..10] do
+# pb.update(i, "Step {i}")
+# end
+# ~~~
+class TermProgress
+
+ # Max value of the progress bar (business value).
+ var max_value: Int
+
+ # Current value of the progress bar (business value).
+ var current_value: Int
+
+ # Number of columns used to display the progress bar.
+ var max_columns = 70 is writable
+
+ # Get the current percent value.
+ fun current_percentage: Int do
+ return current_value * 100 / max_value
+ end
+
+ # Display the progress bar.
+ #
+ # `metadata` can be used to pass a small amount of data to display after
+ # the progressbar.
+ fun display(metadata: nullable String) do
+ var percent = current_percentage
+ var p = current_value * max_columns / max_value
+ printn "\r{percent}% ["
+ for i in [1..max_columns] do
+ if i < p then
+ printn "="
+ else if i == p then
+ printn ">"
+ else
+ printn " "
+ end
+ end
+ printn "]"
+ if metadata != null then printn " ({metadata})"
+ end
+
+ # Update and display the progresssbar.
+ #
+ # See `display`.
+ fun update(new_current: Int, metadata: nullable String) do
+ current_value = new_current
+ display(metadata)
+ end
+end
import collection::array
intrude import text::flat
+# Any kind of entity which can be searched for in a Sequence of Byte
+interface BytePattern
+ # Return the first occurence of `self` in `b`, or -1 if not found
+ fun first_index_in(b: SequenceRead[Byte]): Int do return first_index_in_from(b, 0)
+
+ # Return the first occurence of `self` in `b` starting at `from`, or -1 if not found
+ fun first_index_in_from(b: SequenceRead[Byte], from: Int): Int is abstract
+
+ # Return the last occurence of `self` in `b`, or -1 if not found
+ fun last_index_in(b: SequenceRead[Byte]): Int do return last_index_in_from(b, b.length - 1)
+
+ # Return the last occurence of `self` in `b`, or -1 if not found
+ fun last_index_in_from(b: SequenceRead[Byte], from: Int): Int is abstract
+
+ # Returns the indexes of all the occurences of `self` in `b`
+ fun search_all_in(b: SequenceRead[Byte]): SequenceRead[Int] is abstract
+
+ # Length of the pattern
+ fun pattern_length: Int is abstract
+
+ # Appends `self` to `b`
+ fun append_to(b: Sequence[Byte]) is abstract
+
+ # Is `self` a prefix for `b` ?
+ fun is_prefix(b: SequenceRead[Byte]): Bool is abstract
+
+ # Is `self` a suffix for `b` ?
+ fun is_suffix(b: SequenceRead[Byte]): Bool is abstract
+end
+
redef class Byte
+ super BytePattern
+
# Write self as a string into `ns` at position `pos`
private fun add_digest_at(ns: NativeString, pos: Int) do
var tmp = (0xF0u8 & self) >> 4
# i.e. this abort is here to please the compiler
abort
end
+
+ redef fun first_index_in_from(b, from) do
+ for i in [from .. b.length[ do if b[i] == self then return i
+ return -1
+ end
+
+ redef fun last_index_in_from(b, from) do
+ for i in [0 .. from].step(-1) do if b[i] == self then return i
+ return -1
+ end
+
+ redef fun search_all_in(b) do
+ var ret = new Array[Int]
+ var pos = 0
+ loop
+ pos = first_index_in_from(b, pos)
+ if pos == -1 then return ret
+ ret.add pos
+ pos += 1
+ end
+ end
+
+ redef fun pattern_length do return 1
+
+ redef fun append_to(b) do b.push self
+
+ # assert 'b'.ascii.is_suffix("baqsdb".to_bytes)
+ # assert not 'b'.ascii.is_suffix("baqsd".to_bytes)
+ redef fun is_suffix(b) do return b.length != 0 and b.last == self
+
+ # assert 'b'.ascii.is_prefix("baqsdb".to_bytes)
+ # assert not 'b'.ascii.is_prefix("aqsdb".to_bytes)
+ redef fun is_prefix(b) do return b.length != 0 and b.first == self
end
# A buffer containing Byte-manipulation facilities
# Uses Copy-On-Write when persisted
class Bytes
super AbstractArray[Byte]
+ super BytePattern
# A NativeString being a char*, it can be used as underlying representation here.
var items: NativeString
init(ns, 0, cap)
end
- redef fun is_empty do return length != 0
+ redef fun pattern_length do return length
+
+ redef fun is_empty do return length == 0
# var b = new Bytes.empty
# b.add 101u8
return items[i]
end
+ # Returns a copy of `self`
+ fun clone: Bytes do
+ var b = new Bytes.with_capacity(length)
+ b.append(self)
+ return b
+ end
+
+ # Trims off the whitespaces at the beginning and the end of `self`
+ #
+ # var b = "102041426E6F1020" .hexdigest_to_bytes
+ # assert b.trim.hexdigest == "41426E6F"
+ #
+ # NOTE: A whitespace is defined here as a byte whose value is <= 0x20
+ fun trim: Bytes do
+ var st = 0
+ while st < length do
+ if self[st] > 0x20u8 then break
+ st += 1
+ end
+ if st >= length then return new Bytes.empty
+ var ed = length - 1
+ while ed > 0 do
+ if self[ed] > 0x20u8 then break
+ ed -= 1
+ end
+ return slice(st, ed - st + 1)
+ end
+
+ # Returns a subset of the content of `self` starting at `from` and of length `count`
+ #
+ # var b = "abcd".to_bytes
+ # assert b.slice(1, 2).hexdigest == "6263"
+ # assert b.slice(-1, 2).hexdigest == "61"
+ # assert b.slice(1, 0).hexdigest == ""
+ # assert b.slice(2, 5).hexdigest == "6364"
+ fun slice(from, count: Int): Bytes do
+ if count <= 0 then return new Bytes.empty
+
+ if from < 0 then
+ count += from
+ if count < 0 then count = 0
+ from = 0
+ end
+
+ if (count + from) > length then count = length - from
+ if count <= 0 then return new Bytes.empty
+
+ var ret = new Bytes.with_capacity(count)
+
+ ret.append_ns(items.fast_cstring(from), count)
+ return ret
+ end
+
+ # Returns a copy of `self` starting at `from`
+ #
+ # var b = "abcd".to_bytes
+ # assert b.slice_from(1).hexdigest == "626364"
+ # assert b.slice_from(-1).hexdigest == "61626364"
+ # assert b.slice_from(2).hexdigest == "6364"
+ fun slice_from(from: Int): Bytes do
+ if from >= length then return new Bytes.empty
+ if from < 0 then from = 0
+ return slice(from, length)
+ end
+
# Returns self as a hexadecimal digest
fun hexdigest: String do
var elen = length * 2
length += ln
end
+ # Appends the bytes of `s` to `selftextextt`
+ fun append_text(s: Text) do
+ for i in s.substrings do
+ append_ns(i.fast_cstring, i.bytelen)
+ end
+ end
+
+ redef fun append_to(b) do b.append self
+
redef fun enlarge(sz) do
if capacity >= sz then return
persisted = false
redef fun iterator do return new BytesIterator.with_buffer(self)
+ redef fun first_index_in_from(b, from) do
+ if is_empty then return -1
+ var fst = self[0]
+ var bpos = fst.first_index_in_from(self, from)
+ for i in [0 .. length[ do
+ if self[i] != b[bpos] then return first_index_in_from(b, bpos + 1)
+ bpos += 1
+ end
+ return bpos
+ end
+
+ redef fun last_index_in_from(b, from) do
+ if is_empty then return -1
+ var lst = self[length - 1]
+ var bpos = lst.last_index_in_from(b, from)
+ for i in [0 .. length[.step(-1) do
+ if self[i] != b[bpos] then return last_index_in_from(b, bpos - 1)
+ bpos -= 1
+ end
+ return bpos
+ end
+
+ redef fun search_all_in(b) do
+ var ret = new Array[Int]
+ var pos = first_index_in_from(b, 0)
+ if pos == -1 then return ret
+ pos = pos + 1
+ ret.add pos
+ loop
+ pos = first_index_in_from(b, pos)
+ if pos == -1 then return ret
+ ret.add pos
+ pos += length
+ end
+ end
+
+ # Splits the content on self when encountering `b`
+ #
+ # var a = "String is string".to_bytes.split_with('s'.ascii)
+ # assert a.length == 3
+ # assert a[0].hexdigest == "537472696E672069"
+ # assert a[1].hexdigest == "20"
+ # assert a[2].hexdigest == "7472696E67"
+ fun split_with(b: BytePattern): Array[Bytes] do
+ var fst = b.search_all_in(self)
+ if fst.is_empty then return [clone]
+ var retarr = new Array[Bytes]
+ var prev = 0
+ for i in fst do
+ retarr.add(slice(prev, i - prev))
+ prev = i + b.pattern_length
+ end
+ retarr.add slice_from(prev)
+ return retarr
+ end
+
+ # Splits `self` in two parts at the first occurence of `b`
+ #
+ # var a = "String is string".to_bytes.split_once_on('s'.ascii)
+ # assert a[0].hexdigest == "537472696E672069"
+ # assert a[1].hexdigest == "20737472696E67"
+ fun split_once_on(b: BytePattern): Array[Bytes] do
+ var spl = b.first_index_in(self)
+ if spl == -1 then return [clone]
+ var ret = new Array[Bytes].with_capacity(2)
+ ret.add(slice(0, spl))
+ ret.add(slice_from(spl + b.pattern_length))
+ return ret
+ end
+
+ # Replaces all the occurences of `this` in `self` by `by`
+ #
+ # var b = "String is string".to_bytes.replace(0x20u8, 0x41u8)
+ # assert b.hexdigest == "537472696E6741697341737472696E67"
+ fun replace(pattern: BytePattern, bytes: BytePattern): Bytes do
+ if is_empty then return new Bytes.empty
+ var pos = pattern.search_all_in(self)
+ if pos.is_empty then return clone
+ var ret = new Bytes.with_capacity(length)
+ var prev = 0
+ for i in pos do
+ ret.append_ns(items.fast_cstring(prev), i - prev)
+ bytes.append_to ret
+ prev = i + pattern.pattern_length
+ end
+ ret.append(slice_from(pos.last + pattern.pattern_length))
+ return ret
+ end
+
+ # Decode `self` from percent (or URL) encoding to a clear string
+ #
+ # Replace invalid use of '%' with '?'.
+ #
+ # assert "aBc09-._~".to_bytes.from_percent_encoding == "aBc09-._~".to_bytes
+ # assert "%25%28%29%3c%20%3e".to_bytes.from_percent_encoding == "%()< >".to_bytes
+ # assert ".com%2fpost%3fe%3dasdf%26f%3d123".to_bytes.from_percent_encoding == ".com/post?e=asdf&f=123".to_bytes
+ # assert "%25%28%29%3C%20%3E".to_bytes.from_percent_encoding == "%()< >".to_bytes
+ # assert "incomplete %".to_bytes.from_percent_encoding == "incomplete ?".to_bytes
+ # assert "invalid % usage".to_bytes.from_percent_encoding == "invalid ? usage".to_bytes
+ # assert "%c3%a9%e3%81%82%e3%81%84%e3%81%86".to_bytes.from_percent_encoding == "éあいう".to_bytes
+ fun from_percent_encoding: Bytes do
+ var tmp = new Bytes.with_capacity(length)
+ var pos = 0
+ while pos < length do
+ var b = self[pos]
+ if b != '%'.ascii then
+ tmp.add b
+ pos += 1
+ continue
+ end
+ if length - pos < 2 then
+ tmp.add '?'.ascii
+ pos += 1
+ continue
+ end
+ var bn = self[pos + 1]
+ var bnn = self[pos + 2]
+ if not bn.is_valid_hexdigit or not bnn.is_valid_hexdigit then
+ tmp.add '?'.ascii
+ pos += 1
+ continue
+ end
+ tmp.add((bn.hexdigit_to_byteval << 4) + bnn.hexdigit_to_byteval)
+ pos += 3
+ end
+ return tmp
+ end
+
+ # Is `b` a prefix of `self` ?
+ fun has_prefix(b: BytePattern): Bool do return b.is_prefix(self)
+
+ # Is `b` a suffix of `self` ?
+ fun has_suffix(b: BytePattern): Bool do return b.is_suffix(self)
+
+ redef fun is_suffix(b) do
+ if length > b.length then return false
+ var j = b.length - 1
+ var i = length - 1
+ while i > 0 do
+ if self[i] != b[j] then return false
+ i -= 1
+ j -= 1
+ end
+ return true
+ end
+
+ redef fun is_prefix(b) do
+ if length > b.length then return false
+ for i in [0 .. length[ do if self[i] != b[i] then return false
+ return true
+ end
end
private class BytesIterator
return new Bytes(nns, len, len)
end
end
+
+# Joins an array of bytes `arr` separated by `sep`
+#
+# assert join_bytes(["String".to_bytes, "is".to_bytes, "string".to_bytes], ' '.ascii).hexdigest == "537472696E6720697320737472696E67"
+fun join_bytes(arr: Array[Bytes], sep: nullable BytePattern): Bytes do
+ if arr.is_empty then return new Bytes.empty
+ sep = sep or else new Bytes.empty
+ var endln = sep.pattern_length * (arr.length - 1)
+ for i in arr do endln += i.length
+ var ret = new Bytes.with_capacity(endln)
+ ret.append(arr.first)
+ for i in [1 .. arr.length[ do
+ sep.append_to(ret)
+ ret.append arr[i]
+ end
+ return ret
+end
# assert b == [10, 20, 2, 3, 50]
fun copy_to(start: Int, len: Int, dest: AbstractArray[E], new_start: Int)
do
- # TODO native one
- var i = len
- while i > 0 do
- i -= 1
- dest[new_start+i] = self[start+i]
+ if start < new_start then
+ var i = len
+ while i > 0 do
+ i -= 1
+ dest[new_start+i] = self[start+i]
+ end
+ else
+ var i = 0
+ while i < len do
+ dest[new_start+i] = self[start+i]
+ i += 1
+ end
end
end
end
end
- redef fun iterator: ArrayIterator[E] do
+ redef fun iterator: IndexedIterator[E] do
var res = _free_iterator
if res == null then return new ArrayIterator[E](self)
res._index = 0
do
assert not_empty: not is_empty
var r = first
- var i = 1
- var l = length
- while i < l do
- self[i-1] = self[i]
- i += 1
- end
- _length = l - 1
+ var l = length-1
+ copy_to(1, l, self, 0)
+ _length = l
return r
end
redef fun unshift(item)
do
- var i = length - 1
- while i >= 0 do
- self[i+1] = self[i]
- i -= 1
+ var l = length
+ if l > 0 then
+ enlarge(l + 1)
+ copy_to(0, l, self, 1)
end
self[0] = item
end
_length = nl
end
+ redef fun copy_to(start, len, dest, new_start)
+ do
+ # Fast code when source and destination are two arrays
+
+ if not dest isa Array[E] then
+ super
+ return
+ end
+
+ # Enlarge dest if required
+ var dest_len = new_start + len
+ if dest_len > dest.length then
+ dest.enlarge(dest_len)
+ dest.length = dest_len
+ end
+
+ # Get underlying native arrays
+ var items = self.items
+ if items == null then return
+ var dest_items = dest.items
+ assert dest_items != null
+
+ # Native copy
+ items.memmove(start, len, dest_items, new_start)
+ end
+
redef fun enlarge(cap)
do
var c = _capacity
redef fun item: E do return _iter.item
- var iter: ArrayIterator[E]
+ var iter: Iterator[E]
end
# Copy `length` items to `dest`.
fun copy_to(dest: NativeArray[E], length: Int) is intern
+
+ # Copy `length` items to `dest` starting from `dest`.
+ fun memmove(start: Int, length: Int, dest: NativeArray[E], dest_start: Int) do
+ # TODO native one
+ if start < dest_start then
+ var i = length
+ while i > 0 do
+ i -= 1
+ dest[dest_start+i] = self[start+i]
+ end
+ else
+ var i = 0
+ while i < length do
+ dest[dest_start+i] = self[start+i]
+ i += 1
+ end
+ end
+ end
+
#fun =(o: NativeArray[E]): Bool is intern
#fun !=(o: NativeArray[E]): Bool is intern
end
--- /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.
+
+# Efficient data structure to access both end of the sequence.
+module circular_array
+
+import array
+
+# Efficient data structure to access both end of the sequence.
+#
+# A circular array offers efficient random access,
+# efficient manipulation for both ends of the structure (push, pop, ) and
+# automatic amortized growth.
+#
+# Therefore it can be used as is or as and efficient queue (FIFO/LIFO)
+class CircularArray[E]
+ super Sequence[E]
+
+ # The low-level storage of the items.
+ #
+ # Internally, there is two main configurations
+ #
+ # One part: from `head` to `tail` (inclusive)
+ #
+ # ~~~raw
+ # ...0123...
+ # h t
+ # ~~~
+ #
+ # Two parts: from `head` to `capacity-1`,
+ # then from `0` to `tail` (both inclusive)
+ # Test with `head > tail`
+ #
+ # ~~~raw
+ # 345....012
+ # t h
+ # ~~~
+ #
+ # For consistency, *length* and *index* are used in the context of the sequence (self) and
+ # *capacity* and *offset* are used in the context of the native array.
+ #
+ # Initially the native is not allocated, the first one is done with `enlarge`
+ private var native: NativeArray[E] is noautoinit
+
+ # The offset of the `first` item in `native`
+ private var head = 0
+
+ # The offset of the `last` item in `native`
+ private var tail: Int = -1
+
+ redef var length = 0
+
+ # Transform an index into an offset.
+ #
+ # The method takes care of the initial gap and the possible wrapping.
+ #
+ # REQUIRE: `0 <= index and index < length`
+ private fun offset(index: Int): Int
+ do
+ assert index >= 0
+ var head = self._head
+ var tail = self._tail
+ var offset = head + index
+
+ if head > tail then
+ # Two parts
+ var capacity = native.length
+ if offset < capacity then
+ return offset
+ end
+ offset -= capacity
+ end
+
+ assert offset <= tail
+ return offset
+ end
+
+ redef fun [](index) do return native[offset(index)]
+
+ redef fun []=(index, item) do
+ var l = length
+ if index == l then
+ push(item)
+ return
+ end
+ native[offset(index)] = item
+ end
+
+ redef fun push(item) do
+ var l = length + 1
+ enlarge(l)
+ length = l
+
+ var native = _native
+ var cap = native.length
+ var t = tail + 1
+ if t >= cap then t -= cap
+
+ native[t] = item
+ tail = t
+ end
+
+ redef fun add_all(items) do
+ enlarge length + items.length
+ super
+ end
+
+ redef fun pop do
+ var l = length - 1
+ assert l >= 0
+ length = l
+
+ var native = _native
+ var t = tail
+ var res = native[t]
+
+ t -= 1
+ if t < 0 then t += native.length
+ tail = t
+
+ return res
+ end
+
+ redef fun unshift(item) do
+ var l = length + 1
+ enlarge(l)
+ length = l
+
+ var native = _native
+ var h = head - 1
+ if h < 0 then h += native.length
+
+ native[h] = item
+ head = h
+ end
+
+ redef fun shift do
+ var l = length - 1
+ assert l >= 0
+ length = l
+
+ var native = _native
+ var h = head
+ var res = native[h]
+
+ h += 1
+ var cap = native.length
+ if h >= cap then h -= cap
+ head = h
+
+ return res
+ end
+
+ # Ensure at least a given capacity
+ #
+ # If the current capacity is enough, then no-op.
+ fun enlarge(capacity: Int)
+ do
+ # First allocation
+ if not isset _native then
+ var new_c = 8
+ while new_c < capacity do new_c *= 2
+ native = new NativeArray[E](new_c)
+ return
+ end
+
+ # Compute more capacity
+ var c = native.length
+ if capacity <= c then return
+ var new_c = c
+ while new_c < capacity do new_c *= 2
+
+ var new_native = new NativeArray[E](new_c)
+
+ # Reallocation: just realign the parts on 0
+ if head > tail then
+ # Two parts
+ native.memmove(head, c-head, new_native, 0)
+ native.memmove(0, tail+1, new_native, c-head)
+ else
+ # One part
+ native.memmove(head, length, new_native, 0)
+ end
+ head = 0
+ tail = length - 1
+ native = new_native
+ end
+
+ redef fun insert(item, index)
+ do
+ # Special insertion at the end (is push)
+ if index >= length then
+ assert index == length
+ push(item)
+ return
+ end
+ assert index >= 0
+
+ var new_len = length + 1
+
+ # TODO be more efficient:
+ # Here, we just allocate a new native and copy everything.
+
+ # Allocate a new native array
+ var c = native.length
+ while c < new_len do c *= 2
+ var new_native = new NativeArray[E](c)
+
+ # Copy everything
+ var i = 0
+ while i < index do
+ new_native[i] = self[i]
+ i += 1
+ end
+ new_native[index] = item
+ var l = length
+ while i < l do
+ new_native[i+1] = self[i]
+ i += 1
+ end
+
+ # Use the new native array
+ length = new_len
+ head = 0
+ tail = new_len - 1
+ native = new_native
+ end
+
+ redef fun clear do
+ length = 0
+ head = 0
+ tail = -1
+ end
+
+ redef fun iterator do return new CircularArrayIterator[E](self)
+end
+
+private class CircularArrayIterator[E]
+ super IndexedIterator[E]
+
+ var array: CircularArray[E]
+
+ redef var index = 0
+ redef fun is_ok do return index < array.length
+ redef fun item do return array[index]
+ redef fun next do index += 1
+end
import range
import list
import array
+import circular_array
import sorter
import hash_collection
import union_find
end
end
- redef fun iterator: HashMapIterator[K, V] do return new HashMapIterator[K,V](self)
+ redef fun iterator do return new HashMapIterator[K,V](self)
redef fun length do return _the_length
# O(1)
redef fun is_empty do return _head == null
- # O(n)
- redef fun length
- do
- var l = 0
- var t = _head
- while t != null do
- l += 1
- t = t.next
- end
- return l
- end
+ # O(1)
+ redef var length = 0
# O(n)
redef fun has(e) do return search_node_after(e, _head) != null
node.prev = _tail
end
_tail = node
+ length += 1
end
# O(1)
_head.prev = node
end
_head = node
+ length += 1
end
# O(n)
_tail.next.prev = _tail
end
_tail = l._tail
+ length += l.length
l.clear
end
else
_tail.next = null
end
+ length -= 1
return node.item
end
else
_head.prev = null
end
+ length -= 1
return node.item
end
# Remove the node (ie. atach prev and next)
private fun remove_node(node: ListNode[E])
do
+ length -= 1
if node.prev == null then
_head = node.next
if node.next == null then
private fun insert_before(element: E, node: ListNode[E])
do
+ length += 1
var nnode = new ListNode[E](element)
var prev = node.prev
if prev == null then
fun substring(from: Int, count: Int): SELFTYPE is abstract
# Iterates on the substrings of self if any
- fun substrings: Iterator[FlatText] is abstract
+ private fun substrings: Iterator[FlatText] is abstract
# Is the current Text empty (== "")
#
end
# All kinds of array-based text representations.
-private abstract class FlatText
+abstract class FlatText
super Text
# Underlying C-String (`char*`)
redef class FlatText
- fun first_byte: Int do return 0
+ # First byte of the NativeString
+ protected fun first_byte: Int do return 0
- fun last_byte: Int do return _bytelen - 1
+ # Last byte of the NativeString
+ protected fun last_byte: Int do return _bytelen - 1
# Cache of the latest position (char) explored in the string
var position: Int = 0
# Support to generate and otherwise manipulate Nit code
module gen_nit
+import template
+
redef class Sys
# Reserved keywords in the Nit language
var keywords: Set[String] is lazy do return new HashSet[String].from([
var methods_in_pointer: Array[String] is lazy do return methods_in_object + [
"free"]
end
+
+# Template of a Nit module to generate Nit code
+class NitModule
+ super Template
+
+ # Header on top of the module, usually the documentation
+ var header: nullable Writable = null is writable
+
+ # The module's name
+ var name: Writable is writable
+
+ # Imports from this module
+ var imports = new Array[Writable]
+
+ # Main content of this module
+ var content = new Array[Writable]
+
+ redef fun rendering
+ do
+ var header = header
+ if header != null then add header
+
+ var name = name
+ add "module {name}\n\n"
+
+ for i in imports do add "import {i}\n"
+ add "\n"
+
+ for l in content do
+ add l
+ add "\n"
+ end
+ end
+end
super
end
+ # Issue id.
+ fun id: Int do return json["id"].as(Int)
+
+ # Set issue id.
+ fun id=(id: Int) do json["id"] = id
+
# Issue title.
fun title: String do return json["title"].as(String)
for obj in array do
if not obj isa JsonObject then continue
var id = obj["id"].as(Int)
- res.add(api.load_issue_comment(repo, id).as(not null))
+ var comment = api.load_issue_comment(repo, id)
+ if comment == null then continue
+ res.add(comment)
end
page += 1
- array = api.get("{key}/comments?page={page}").as(JsonArray)
+ var json = api.get("{key}/comments?page={page}")
+ if not json isa JsonArray then
+ return res
+ end
+ array = json
end
return res
end
self.json = json
end
+ # Event ID from Github.
+ fun id: String do return json["id"].as(String)
+
+ # Set id.
+ fun id=(id: String) do json["id"] = id
+
# Action performed by the event.
fun action: String do return json["action"].as(String)
struct hostent *hostent = gethostbyname(address);
+ if (!hostent) {
+ return NULL;
+ }
+
memset(&sin, 0, sizeof(sin));
sin.sin_family = hostent->h_addrtype;
sin.sin_port = htons(port);
# * [Binary JSON spec](http://bsonspec.org/)
# * [Libbson](http://api.mongodb.org/libbson/1.1.4/)#
private class BSON
- super Finalizable
+ super FinalizableOnce
# Native instance pointer.
var native: NativeBSON
- # Is the native instance valid?
- #
- # This is set to false if the `native` is destroyed.
- var is_alive = true
-
# Returns a new BSON object initialized from the content of `json`.
#
# ~~~
end
redef fun to_s do
- assert is_alive
var ns = native.to_native_string
var res = ns.to_s_with_copy
ns.free # manual free of gc allocated NativeString
# assert json["ELS"].as(JsonArray).is_empty
# ~~~
fun to_json: JsonObject do
- assert is_alive
- return to_s.parse_json.as(JsonObject)
- end
-
- redef fun finalize do
- if is_alive then
- native.destroy
- is_alive = false
+ var json = to_s.parse_json
+ if json isa JsonParseError then
+ print json.message
+ sys.exit 1
end
+ return json.as(JsonObject)
end
+
+ redef fun finalize_once do native.destroy
end
redef class JsonObject
private var native: BSONError
- # Is the native instance valid?
- #
- # This is set to false if the `native` is destroyed.
- private var is_alive = true
-
# Logical domain within a library that created the error.
- fun domain: Int do
- assert is_alive
- return native.domain
- end
+ fun domain: Int do return native.domain
# Domain specific error code.
- fun code: Int do
- assert is_alive
- return native.code
- end
+ fun code: Int do return native.code
# Human readable error message.
fun message: String do
- assert is_alive
var ns = native.message
var res = ns.to_s_with_copy
ns.free
# assert client.server_uri == uri
# ~~~
class MongoClient
- super Finalizable
+ super FinalizableOnce
# Server URI.
var server_uri: String
private var native: NativeMongoClient is noinit
- # Is the native instance valid?
- #
- # This is set to false if the `native` is destroyed.
- private var is_alive = true
-
- init do
- native = new NativeMongoClient(server_uri.to_cstring)
- end
+ init do native = new NativeMongoClient(server_uri.to_cstring)
# Gets server data.
#
# assert client.server_status["process"] == "mongod"
# ~~~
fun server_status: nullable JsonObject do
- assert is_alive
var nbson = native.server_status
if nbson == null then return null
var bson = new BSON(nbson)
# assert client.database_names.has("test")
# ~~~
fun database_names: Array[String] do
- assert is_alive
var res = new Array[String]
var nas = native.database_names
if nas == null then return res
# var client = new MongoClient("mongodb://localhost:27017/")
# assert client.database("test").name == "test"
# ~~~
- fun database(name: String): MongoDb do
- assert is_alive
- return new MongoDb(self, name)
- end
+ fun database(name: String): MongoDb do return new MongoDb(self, name)
# Close the connexion and destroy the instance.
#
# The reference should not be used beyond this point!
- fun close do
- assert is_alive
- finalize
- end
+ fun close do finalize_once
- redef fun finalize do
- if is_alive then
- native.destroy
- is_alive = false
- end
- end
+ redef fun finalize_once do native.destroy
# Last error raised by mongoc.
fun last_error: nullable MongoError do
# first document into a collection.
# There is no need to create a database manually.
class MongoDb
- super Finalizable
+ super FinalizableOnce
# `MongoClient` used to load this database.
var client: MongoClient
private var native: NativeMongoDb is noinit
- # Is the native instance valid?
- #
- # This is set to false if the `native` is destroyed.
- private var is_alive = true
-
- init do
- native = new NativeMongoDb(client.native, name.to_cstring)
- end
+ init do native = new NativeMongoDb(client.native, name.to_cstring)
# Lists available collection names.
#
# assert db.collection_names.has("test")
# ~~~
fun collection_names: Array[String] do
- assert is_alive
var res = new Array[String]
var nas = native.collection_names
if nas == null then return res
# assert col.name == "test"
# ~~~
fun collection(name: String): MongoCollection do
- assert is_alive
return new MongoCollection(self, name)
end
# assert not db.has_collection("qwerty")
# ~~~
fun has_collection(name: String): Bool do
- assert is_alive
# TODO handle error
return native.has_collection(name.to_cstring)
end
# Drop `self`, returns false if an error occured.
- fun drop: Bool do
- assert is_alive
- return native.drop
- end
+ fun drop: Bool do return native.drop
- redef fun finalize do
- if is_alive then
- native.destroy
- is_alive = false
- end
- end
+ redef fun finalize_once do native.destroy
end
# A Mongo collection.
# the first document.
# There is no need to create a database manually.
class MongoCollection
- super Finalizable
+ super FinalizableOnce
# Database that collection belongs to.
var database: MongoDb
private var native: NativeMongoCollection is noinit
- # Is the native instance valid?
- #
- # This is set to false if the `native` is destroyed.
- private var is_alive = true
-
# Loads a collection.
#
# Call `MongoDb::collection` instead.
# assert doc.has_key("_id")
# ~~~
fun insert(doc: JsonObject): Bool do
- assert is_alive
var res = native.insert(doc.to_bson.native)
if res then set_id(doc)
return res
#
# See `insert`.
fun insert_all(docs: Collection[JsonObject]): Bool do
- assert is_alive
var res = true
for doc in docs do res = insert(doc) and res
return res
# assert doc["_id"] == id
# ~~~
fun save(doc: JsonObject): Bool do
- assert is_alive
- var res = native.save(doc.to_bson.native)
+ var bson = doc.to_bson
+ var nat = bson.native
+ var res = native.save(nat)
if res then set_id(doc)
+ assert nat != self #FIXME used to avoid GC crashes
+ assert bson != self #FIXME used to avoid GC crashes
return res
end
# assert col.remove(sel)
# ~~~
fun remove(selector: JsonObject): Bool do
- assert is_alive
return native.remove(selector.to_bson.native)
end
#
# See `remove`.
fun remove_all(selector: JsonObject): Bool do
- assert is_alive
return native.remove_all(selector.to_bson.native)
end
# assert col.update(sel, upd)
# ~~~
fun update(selector: JsonObject, update: JsonObject): Bool do
- assert is_alive
return native.update(
selector.to_bson.native,
update.to_bson.native)
# assert col.count(query) > 0
# ~~~
fun count(query: JsonObject): Int do
- assert is_alive
return native.count(query.to_bson.native)
end
# Finds the first document that matches `query`.
#
+ # Params:
+ # * `skip` number of documents to skip
+ # * `limit` number of documents to return
+ #
# Returns `null` if an error occured. See `Sys::last_mongoc_error`.
#
# ~~~
# var doc = col.find(query)
# assert doc["foo"] == 10
# ~~~
- fun find(query: JsonObject): nullable JsonObject do
- assert is_alive
+ fun find(query: JsonObject, skip, limit: nullable Int): nullable JsonObject do
var q = new NativeBSON.from_json_string(query.to_json.to_cstring)
- var c = native.find(q)
+ var s = skip or else 0
+ var l = limit or else 0
+ var c = native.find(q, s, l)
q.destroy
if c == null then return null
var cursor = new MongoCursor(c)
- if cursor.is_ok then return cursor.item
- return null
+ if not cursor.is_ok then
+ return null
+ end
+ var item = cursor.item
+ assert cursor != self
+ return item
end
# Finds all the documents matching the `query`.
#
+ # Params:
+ # * `skip` number of documents to skip
+ # * `limit` number of documents to return
+ #
# ~~~
# var client = new MongoClient("mongodb://localhost:27017/")
# var col = client.database("test").collection("test")
# query["foo"] = 10
# assert col.find_all(query).length > 0
# ~~~
- fun find_all(query: JsonObject): Array[JsonObject] do
- assert is_alive
+ fun find_all(query: JsonObject, skip, limit: nullable Int): Array[JsonObject] do
+ var s = skip or else 0
+ var l = limit or else 0
var res = new Array[JsonObject]
- var c = native.find(query.to_bson.native)
+ var c = native.find(query.to_bson.native, s, l)
if c == null then return res
var cursor = new MongoCursor(c)
- for item in cursor do res.add item
+ while cursor.is_ok do
+ res.add cursor.item
+ cursor.next
+ end
return res
end
# assert col.stats["ns"] == "test.test"
# ~~~
fun stats: nullable JsonObject do
- assert is_alive
var bson = native.stats
if bson == null then return null
return new JsonObject.from_bson(new BSON(bson))
end
# Drops `self`, returns false if an error occured.
- fun drop: Bool do
- assert is_alive
- return native.drop
- end
+ fun drop: Bool do return native.drop
# Moves `self` to another `database`.
#
# this collection after the move.
# Additional operations will occur on moved collection.
fun move(database: MongoDb): Bool do
- assert is_alive
self.database = database
return native.rename(database.name.to_cstring, name.to_cstring)
end
# to continue using this collection after the rename.
# Additional operations will occur on renamed collection.
fun rename(name: String): Bool do
- assert is_alive
self.name = name
return native.rename(database.name.to_cstring, name.to_cstring)
end
- redef fun finalize do
- if is_alive then
- native.destroy
- is_alive = false
- end
- end
+ redef fun finalize_once do native.destroy
end
# A MongoDB query cursor.
# It wraps up the wire protocol negotation required to initiate a query and
# retreive an unknown number of documents.
class MongoCursor
- super Finalizable
+ super FinalizableOnce
super Iterator[JsonObject]
private var native: NativeMongoCursor
- # Is the native instance valid?
- #
- # This is set to false if the `native` is destroyed.
- private var is_alive = true
-
init do next
- redef fun is_ok do
- assert is_alive
- return native.more
- end
+ redef var is_ok = true
- redef fun next do
- assert is_alive
- native.next
- end
+ redef fun next do is_ok = native.next
redef fun item do
- assert is_alive
return new JsonObject.from_bson(new BSON(native.current))
end
- redef fun finalize do
- if is_alive then
- native.destroy
- is_alive = false
- end
- end
+ redef fun finalize_once do native.destroy
end
#
# If you would like to specify options such as a sort order,
# the query must be placed inside of `{"$query": {}}`.
- fun find(query: NativeBSON): nullable NativeMongoCursor import
+ fun find(query: NativeBSON, skip, limit: Int): nullable NativeMongoCursor import
NativeMongoCursor.as nullable, set_mongoc_error `{
bson_error_t error;
mongoc_cursor_t *cursor;
- cursor = mongoc_collection_find(self, MONGOC_QUERY_NONE, 0, 0, 0, query, NULL, NULL);
+ cursor = mongoc_collection_find(self, MONGOC_QUERY_NONE, skip, limit, 0, query, NULL, NULL);
if (mongoc_cursor_error(cursor, &error)) {
NativeMongoCollection_set_mongoc_error(self, &error);
return null_NativeMongoCursor();
# Wrapper for `mongoc_cursor_current()`.
#
# Fetches the cursors current document or NULL if there has been an error.
- fun current: NativeBSON `{ return (bson_t*) mongoc_cursor_current(self); `}
+ fun current: NativeBSON `{
+ // As said in documentation, BSON objects should not be freed manually.
+ bson_t* bson = (bson_t*) mongoc_cursor_current(self);
+ // Copy BSON so we can let the GC free it automatically.
+ return bson_copy(bson);
+ `}
# Wrapper for `mongoc_cursor_next()`.
#
return mongoc_cursor_next(self, &doc);
`}
- # Wrapper for `mongoc_cursor_more()`.
- #
- # This function shall indicate if there is more data to be read from the cursor.
- fun more: Bool `{ return mongoc_cursor_more(self); `}
-
# Wrapper for `mongoc_cursor_destroy()`.
#
# This instance should not be used beyond this point!
--- /dev/null
+src/restful_annot_gen.nit
-all:
+all: bin/restful_annot
mkdir -p bin/
../../../bin/nitc --dir bin src/nitcorn_hello_world.nit src/simple_file_server.nit
xymus.net:
mkdir -p bin/
../../../bin/nitc --dir bin/ src/xymus_net.nit
+
+pre-build: src/restful_annot_gen.nit
+src/restful_annot_gen.nit:
+ ../../../bin/nitrestful -o $@ src/restful_annot.nit
+
+bin/restful_annot: src/restful_annot_gen.nit
+ mkdir -p bin/
+ ../../../bin/nitc -o $@ src/restful_annot_gen.nit
--- /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.
+
+import nitcorn::restful
+
+class MyAction
+ super RestfulAction
+
+ # Method answering requests like `foo?s=some_string&i=42&b=true`
+ fun foo(s: String, i: Int, b: Bool): HttpResponse
+ is restful do
+ var resp = new HttpResponse(200)
+ resp.body = "foo {s} {i} {b}"
+ return resp
+ end
+
+ # Method answering requests like `bar?s=these_arguments_are_optional`
+ fun bar(s: nullable String, i: nullable Int, b: nullable Bool): HttpResponse
+ is restful do
+ var resp = new HttpResponse(200)
+ resp.body = "bar {s or else "null"} {i or else "null"} {b or else "null"}"
+ return resp
+ end
+end
+
+var vh = new VirtualHost("localhost:8080")
+
+# Serve everything with our restful action
+vh.routes.add new Route(null, new MyAction)
+
+# Avoid executing when running tests
+if "NIT_TESTING".environ == "true" then exit 0
+
+var factory = new HttpFactory.and_libevent
+factory.config.virtual_hosts.add vh
+factory.run
--- /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.
+
+# Services inserting a timestamp in all prints and to log each requests
+# Also keep track of the performances of the requests
+module log
+
+import reactor
+import realtime
+import performance_analysis
+
+redef class Action
+
+ redef fun prepare_respond_and_close(request, truncated_uri, http_server) do
+ if not log_nitcorn_actions then
+ super
+ return
+ end
+ print """{{{class_name}}} enter:
+uri="{{{truncated_uri}}}"
+query="{{{request.query_string}}}"
+body:{{{request.body.length}}} bytes"""
+ var clock = new Clock
+ super
+ var perf = sys.perfs[class_name]
+ perf.add(clock.lapse)
+ if perf.count % perfs_print_period == 0 then print "{class_name} perfs: {perf}:"
+ print "{class_name} return: uri={truncated_uri}"
+ end
+end
+
+redef fun print(object) do
+ var timestamp = new Tm.gmtime
+ super "{timestamp.year}/{timestamp.mon}/{timestamp.mday} "+
+ "{timestamp.hour}:{timestamp.min}:{timestamp.sec}: {object}"
+end
+
+redef fun print_error(object) do
+ var timestamp = new Tm.gmtime
+ super "{timestamp.year}/{timestamp.mon}/{timestamp.mday} "+
+ "{timestamp.hour}:{timestamp.min}:{timestamp.sec}: {object}"
+end
+
+# Should the actions be logged ?
+fun log_nitcorn_actions: Bool do return false
+
+# Number of actions executed before printing the perfs
+fun perfs_print_period: Int do return 20
--- /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.
+
+# Support module for the `nitrestful` tool and the `restful` annotation
+module restful is new_annotation(restful)
+
+import nitcorn
+import json::serialization
+
+# Action with `restful` methods
+class RestfulAction
+ super Action
+
+ redef fun answer(request, truncated_uri) do return new HttpResponse(400)
+
+ # Service to deserialize arguments from JSON
+ #
+ # Accepts `nullable String` for convenience, but returns `null` when `val == null`.
+ #
+ # This method is called by the code generated by `nitrestful`.
+ # It can be specialized to customize its behavior.
+ protected fun deserialize_arg(val: nullable String): nullable Object
+ do
+ if val == null then return null
+
+ var deserializer = new JsonDeserializer(val)
+ if deserializer.errors.not_empty then
+ print_error deserializer.errors.join("\n")
+ return null
+ end
+
+ return deserializer.deserialize
+ end
+end
--- /dev/null
+# NAME
+
+nitrestful - generates boilerplate code to relay RESTful request to static Nit methods
+
+# SYNOPSIS
+
+nitrestful [*options*]... FILE
+
+# OPTIONS
+
+### `-o`, `--output`
+Output file (can also be 'stdout').
+
+### `--dir`
+Output directory.
+
+# SEE ALSO
+
+The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
NITCOPT=--semi-global
OLDNITCOPT=--semi-global
-OBJS=nitc nitpick nit nitdoc nitls nitunit nitpretty nitmetrics nitx nitlight nitdbg_client nitserial
+OBJS=nitc nitpick nit nitdoc nitls nitunit nitpretty nitmetrics nitx nitlight nitdbg_client nitserial nitrestful
SRCS=$(patsubst %,%.nit,$(OBJS))
BINS=$(patsubst %,../bin/%,$(OBJS))
do
node.accept_ast_validation(self)
end
- private var path = new List[ANode]
+ private var path = new CircularArray[ANode]
private var seen = new HashSet[ANode]
end
fun compile_callback_to_cpp(mmodule: MModule, mainmodule: MModule) do end
end
-fun cpp_call_context: CppCallContext do return once new CppCallContext
-fun to_cpp_call_context: ToCppCallContext do return once new ToCppCallContext
-fun from_cpp_call_context: FromCppCallContext do return once new FromCppCallContext
+private fun cpp_call_context: CppCallContext do return once new CppCallContext
+private fun to_cpp_call_context: ToCppCallContext do return once new ToCppCallContext
+private fun from_cpp_call_context: FromCppCallContext do return once new FromCppCallContext
redef class MExplicitCall
redef fun compile_callback_to_cpp(mmodule, mainmodule)
end
end
-# TODO add annotations on attributes (volatile, sensitive or do_not_serialize?)
private class SerializationPhasePreModel
super Phase
end
end
- if mysignature.arity > 0 then
+ if nsig != null then
# Check parameters visibility
for i in [0..mysignature.arity[ do
var nt = nsig.n_params[i].n_type
--- /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.
+
+# Tool generating boilerplate code linking RESTful actions to Nit methods
+module nitrestful
+
+import gen_nit
+
+import frontend
+
+private class RestfulPhase
+ super Phase
+
+ # Classes with methods marked with the `restful` annotation
+ var restful_classes = new HashSet[MClass]
+
+ redef fun process_annotated_node(node, nat)
+ do
+ # Skip if we are not interested
+ var text = nat.n_atid.n_id.text
+ if text != "restful" then return
+
+ if not node isa AMethPropdef then
+ toolcontext.error(nat.location,
+ "Syntax Error: `restful` can only be applied on method definitions")
+ return
+ end
+
+ var mpropdef = node.mpropdef
+ if mpropdef == null then return
+
+ var mproperty = mpropdef.mproperty
+ var mclassdef = mpropdef.mclassdef
+ var mmodule = mclassdef.mmodule
+
+ # Test subclass of `RestfulAction`
+ var sup_class_name = "RestfulAction"
+ var sup_class = toolcontext.modelbuilder.try_get_mclass_by_name(
+ nat, mmodule, sup_class_name)
+ var in_hierarchy = mclassdef.in_hierarchy
+ if in_hierarchy == null or sup_class == null then return
+ var sup_classes = in_hierarchy.greaters
+ if not sup_classes.has(sup_class.intro) then
+ toolcontext.error(nat.location,
+ "Syntax Error: `restful` is only valid within subclasses of `{sup_class_name}`")
+ return
+ end
+
+ # Register the property
+ var mclass = mclassdef.mclass
+ mclass.restful_methods.add mproperty
+ restful_classes.add mclass
+ end
+end
+
+redef class MClass
+
+ # Methods with the `restful` annotation in this class
+ private var restful_methods = new Array[MMethod]
+end
+
+redef class ToolContext
+ # Generate serialization and deserialization methods on `auto_serializable` annotated classes.
+ var restful_phase: Phase = new RestfulPhase(self, [modelize_class_phase])
+
+ # Where do we put a single result?
+ var opt_output: OptionString = new OptionString("Output file (can also be 'stdout')", "-o", "--output")
+
+ # Where do we put the result?
+ var opt_dir: OptionString = new OptionString("Output directory", "--dir")
+
+ redef init
+ do
+ option_context.add_option(opt_output, opt_dir)
+ super
+ end
+end
+
+redef class MType
+ # Write code in `template` to parse the argument `arg_name` to this parameter type
+ private fun gen_arg_convert(template: Template, arg_name: String)
+ do
+ if self.name == "String" or self.name == "nullable String" then
+ # String are used as is
+ template.add """
+ var out_{{{arg_name}}} = in_{{{arg_name}}}
+"""
+ else
+ # Deserialize everything else
+ template.add """
+ var out_{{{arg_name}}} = deserialize_arg(in_{{{arg_name}}})
+"""
+ end
+ end
+
+ # Does this parameter type needs to be checked before calling the method?
+ #
+ # Some nullable types do not need to be check as `null` values are acceptable.
+ private fun needs_type_check: Bool do return true
+end
+
+redef class MNullableType
+ redef fun needs_type_check do return name != "nullable String" and name != "nullable Object"
+end
+
+var toolcontext = new ToolContext
+toolcontext.tooldescription = """
+Usage: nitrestful [OPTION] module.nit [other_module.nit [...]]
+Generates the boilerplate code to link RESTful request to static Nit methods."""
+
+toolcontext.process_options args
+var arguments = toolcontext.option_context.rest
+
+# Check options
+if toolcontext.opt_output.value != null and toolcontext.opt_dir.value != null then
+ print "Error: cannot use both --dir and --output"
+ exit 1
+end
+if arguments.length > 1 and toolcontext.opt_output.value != null then
+ print "Error: --output needs a single source file. Do you prefer --dir?"
+ exit 1
+end
+
+var model = new Model
+var modelbuilder = new ModelBuilder(model, toolcontext)
+
+var mmodules = modelbuilder.parse(arguments)
+modelbuilder.run_phases
+var first_mmodule = mmodules.first
+
+# Name of the support module
+var module_name
+
+# Path to the support module
+var module_path = toolcontext.opt_output.value
+if module_path == null then
+ module_name = "{first_mmodule.name}_rest"
+ module_path = "{module_name}.nit"
+
+ var dir = toolcontext.opt_dir.value
+ if dir != null then module_path = dir.join_path(module_path)
+else if module_path == "stdout" then
+ module_name = "{first_mmodule.name}_rest"
+ module_path = null
+else if module_path.has_suffix(".nit") then
+ module_name = module_path.basename(".nit")
+else
+ module_name = module_path.basename
+ module_path += ".nit"
+end
+
+var nit_module = new NitModule(module_name)
+nit_module.header = """
+# This file is generated by nitrestful
+# Do not modify, instead refine the generated services.
+"""
+
+for mmod in mmodules do
+ nit_module.imports.add mmod.name
+end
+
+var phase = toolcontext.restful_phase
+assert phase isa RestfulPhase
+
+for mclass in phase.restful_classes do
+
+ var t = new Template
+ nit_module.content.add t
+
+ t.add """
+redef class {{{mclass}}}
+ redef fun answer(request, truncated_uri)
+ do
+ var verbs = truncated_uri.split("/")
+ if verbs.not_empty and verbs.first.is_empty then verbs.shift
+
+ if verbs.length != 1 then return super
+ var verb = verbs.first
+
+"""
+ var methods = mclass.restful_methods
+ for i in methods.length.times, method in methods do
+ var msig = method.intro.msignature
+ if msig == null then continue
+
+ t.add " "
+ if i != 0 then t.add "else "
+
+ t.add """if verb == "{{{method.name}}}" then
+"""
+
+ var args = new Array[String]
+ var isas = new Array[String]
+ for param in msig.mparameters do
+
+ t.add """
+ var in_{{{param.name}}} = request.string_arg("{{{param.name}}}")
+"""
+
+ var mtype = param.mtype
+ mtype.gen_arg_convert(t, param.name)
+
+ var arg = "out_{param.name}"
+ args.add arg
+
+ if mtype.needs_type_check then
+ isas.add "{arg} isa {mtype.name}"
+ end
+
+ t.add "\n"
+ end
+
+ if isas.not_empty then t.add """
+ if not {{{isas.join(" or not ")}}} then
+ return super
+ end
+"""
+
+ var sig = ""
+ if args.not_empty then sig = "({args.join(", ")})"
+
+ t.add """
+ return {{{method.name}}}{{{sig}}}
+"""
+ end
+
+ t.add """
+ end
+ return super
+ end
+end"""
+end
+
+# Write support module
+if module_path != null then
+ # To file
+ nit_module.write_to_file module_path
+else
+ # To stdout
+ nit_module.write_to stdout
+end
# generate and include its own serialization support module.
module nitserial
-import frontend
-import rapid_type_analysis
import template
+import gen_nit
-# A Nit module
-#
-# TODO add more features and move to lib
-class NitModule
- super Template
-
- var header: nullable Writable = null
-
- # The module's name
- var name: Writable
-
- # Imports from this module
- var imports = new Array[Writable]
-
- # Main content of this module
- var content = new Array[Writable]
-
- redef fun rendering
- do
- var header = header
- if header != null then add header
-
- var name = name
- add "module {name}\n\n"
-
- for i in imports do add "import {i}\n"
- add "\n"
-
- for l in content do add "{l}\n"
- end
-end
+import frontend
+import rapid_type_analysis
redef class ToolContext
# Where do we put a single result?
import semantize
-private import csv # for live_types_to_csv
+import csv # for live_types_to_csv
private import ordered_tree # for live_methods_to_tree
private import more_collections
protected fun proA(a: A) do end
private fun priA(a: A) do end
+ fun pubA2: A do abort
+ protected fun proA2: A do abort
+ private fun priA2: A do abort
+
var vpubA: nullable A is writable, noinit
protected var vproA: nullable A is protected writable, noinit
private var vpriA: nullable A is noinit
#alt2#protected fun proB(a: B) do end
private fun priB(a: B) do end
+ #alt1#fun pubB2: B do abort
+ #alt2#protected fun proB2: B do abort
+ private fun priB2: B do abort
+
#alt3#var vpubB: nullable B is writable, noinit
#alt4#protected var vproB: nullable B is protected writable, noinit
private var vpriB: nullable B is noinit
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2005-2008 Jean Privat <jean@pryen.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.
+
+import more_collections
+
+fun test_list(l: Sequence[Int], n: Int)
+do
+ for i in [0..n[ do l.push i
+ print "{l.length} {l.first} {l.last}"
+ for i in [0..n[ do l.unshift -i-1
+ print "{l.length} {l.first} {l.last}"
+ for i in [0..n[ do l.unshift l.pop
+ print "{l.length} {l.first} {l.last}"
+ for i in [0..n[ do l.push l.shift
+ print "{l.length} {l.first} {l.last}"
+ for i in [0..n[ do l[i] = l[n+i]
+ print "{l.length} {l.first} {l.last}"
+ for i in [0..n] do l.insert(i*10, i*2)
+ print "{l.length} {l.first} {l.last}"
+ print ""
+end
+
+var nb = 100
+if args.not_empty then nb = args.first.to_i
+
+test_list(new Array[Int], nb)
+test_list(new List[Int], nb)
+test_list(new UnrolledList[Int], nb)
+test_list(new CircularArray[Int], nb)
pep8analysis
emscripten
nitserial_args
+nitrestful_args
nitunit_args
nitpretty_args
hamming_number
--- /dev/null
+-o stdout ../lib/nitcorn/examples/src/restful_annot.nit
pep8analysis
emscripten
nitserial_args
+nitrestful_args
nitunit_args
nitpretty_args
hamming_number
-alt/base_prot_sig_alt1.nit:29,14: Error: the public property `pubB` cannot contain the private type `B`.
+alt/base_prot_sig_alt1.nit:33,14: Error: the public property `pubB` cannot contain the private type `B`.
+alt/base_prot_sig_alt1.nit:37,13: Error: the public property `pubB2` cannot contain the private type `B`.
-alt/base_prot_sig_alt2.nit:30,24: Error: the protected property `proB` cannot contain the private type `B`.
+alt/base_prot_sig_alt2.nit:34,24: Error: the protected property `proB` cannot contain the private type `B`.
+alt/base_prot_sig_alt2.nit:38,23: Error: the protected property `proB2` cannot contain the private type `B`.
-alt/base_prot_sig_alt3.nit:33,13--22: Error: the public property `vpubB` cannot contain the private type `B`.
-alt/base_prot_sig_alt3.nit:33,13--22: Error: the public property `vpubB=` cannot contain the private type `B`.
+alt/base_prot_sig_alt3.nit:41,13--22: Error: the public property `vpubB` cannot contain the private type `B`.
+alt/base_prot_sig_alt3.nit:41,13--22: Error: the public property `vpubB=` cannot contain the private type `B`.
-alt/base_prot_sig_alt4.nit:34,23--32: Error: the protected property `vproB` cannot contain the private type `B`.
-alt/base_prot_sig_alt4.nit:34,23--32: Error: the protected property `vproB=` cannot contain the private type `B`.
+alt/base_prot_sig_alt4.nit:42,23--32: Error: the protected property `vproB` cannot contain the private type `B`.
+alt/base_prot_sig_alt4.nit:42,23--32: Error: the protected property `vproB=` cannot contain the private type `B`.
-alt/base_prot_sig_alt5.nit:37,6--11: Error: the public property `vpubB2` cannot contain the private type `B`.
-alt/base_prot_sig_alt5.nit:37,6--11: Error: the public property `vpubB2=` cannot contain the private type `B`.
+alt/base_prot_sig_alt5.nit:45,6--11: Error: the public property `vpubB2` cannot contain the private type `B`.
+alt/base_prot_sig_alt5.nit:45,6--11: Error: the public property `vpubB2=` cannot contain the private type `B`.
-alt/base_prot_sig_alt6.nit:38,16--21: Error: the protected property `vproB2` cannot contain the private type `B`.
-alt/base_prot_sig_alt6.nit:38,16--21: Error: the protected property `vproB2=` cannot contain the private type `B`.
+alt/base_prot_sig_alt6.nit:46,16--21: Error: the protected property `vproB2` cannot contain the private type `B`.
+alt/base_prot_sig_alt6.nit:46,16--21: Error: the protected property `vproB2=` cannot contain the private type `B`.
-alt/base_prot_sig_alt7.nit:46,2--10: Error: `private` is the only legal visibility for properties in a private class.
-alt/base_prot_sig_alt7.nit:50,2--10: Error: `private` is the only legal visibility for properties in a private class.
-alt/base_prot_sig_alt7.nit:50,37--45: Error: `private` is the only legal visibility for properties in a private class.
alt/base_prot_sig_alt7.nit:54,2--10: Error: `private` is the only legal visibility for properties in a private class.
-alt/base_prot_sig_alt7.nit:54,34--42: Error: `private` is the only legal visibility for properties in a private class.
alt/base_prot_sig_alt7.nit:58,2--10: Error: `private` is the only legal visibility for properties in a private class.
+alt/base_prot_sig_alt7.nit:58,37--45: Error: `private` is the only legal visibility for properties in a private class.
alt/base_prot_sig_alt7.nit:62,2--10: Error: `private` is the only legal visibility for properties in a private class.
-alt/base_prot_sig_alt7.nit:62,37--45: Error: `private` is the only legal visibility for properties in a private class.
+alt/base_prot_sig_alt7.nit:62,34--42: Error: `private` is the only legal visibility for properties in a private class.
alt/base_prot_sig_alt7.nit:66,2--10: Error: `private` is the only legal visibility for properties in a private class.
-alt/base_prot_sig_alt7.nit:66,34--42: Error: `private` is the only legal visibility for properties in a private class.
+alt/base_prot_sig_alt7.nit:70,2--10: Error: `private` is the only legal visibility for properties in a private class.
+alt/base_prot_sig_alt7.nit:70,37--45: Error: `private` is the only legal visibility for properties in a private class.
+alt/base_prot_sig_alt7.nit:74,2--10: Error: `private` is the only legal visibility for properties in a private class.
+alt/base_prot_sig_alt7.nit:74,34--42: Error: `private` is the only legal visibility for properties in a private class.
--- /dev/null
+100 0 99
+200 -100 99
+200 0 -1
+200 -100 99
+200 0 99
+301 0 99
+
+100 0 99
+200 -100 99
+200 0 -1
+200 -100 99
+200 0 99
+301 0 99
+
+100 0 99
+200 -100 99
+200 0 -1
+200 -100 99
+200 0 99
+301 0 99
+
+100 0 99
+200 -100 99
+200 0 -1
+200 -100 99
+200 0 99
+301 0 99
+
--- /dev/null
+Usage: nitrestful [OPTION] module.nit [other_module.nit [...]]
+Generates the boilerplate code to link RESTful request to static Nit methods.
+Use --help for help
--- /dev/null
+# This file is generated by nitrestful
+# Do not modify, instead refine the generated services.
+module restful_annot_rest
+
+import restful_annot
+
+redef class MyAction
+ redef fun answer(request, truncated_uri)
+ do
+ var verbs = truncated_uri.split("/")
+ if verbs.not_empty and verbs.first.is_empty then verbs.shift
+
+ if verbs.length != 1 then return super
+ var verb = verbs.first
+
+ if verb == "foo" then
+ var in_s = request.string_arg("s")
+ var out_s = in_s
+
+ var in_i = request.string_arg("i")
+ var out_i = deserialize_arg(in_i)
+
+ var in_b = request.string_arg("b")
+ var out_b = deserialize_arg(in_b)
+
+ if not out_s isa String or not out_i isa Int or not out_b isa Bool then
+ return super
+ end
+ return foo(out_s, out_i, out_b)
+ else if verb == "bar" then
+ var in_s = request.string_arg("s")
+ var out_s = in_s
+
+ var in_i = request.string_arg("i")
+ var out_i = deserialize_arg(in_i)
+
+ var in_b = request.string_arg("b")
+ var out_b = deserialize_arg(in_b)
+
+ if not out_i isa nullable Int or not out_b isa nullable Bool then
+ return super
+ end
+ return bar(out_s, out_i, out_b)
+ end
+ return super
+ end
+end
Object -> Task [dir=back arrowtail=open style=dashed];
A [
- label = "{A|- _vpubA: nullable A\l- _vproA: nullable A\l- _vpriA: nullable A\l- _vpubA2: A\l- _vproA2: A\l- _vpriA2: A\l- _vpriB: nullable B\l- _vpriB2: B\l|+ pubA(a: A)\l# proA(a: A)\l- priA(a: A)\l+ vpubA(): nullable A\l+ vpubA=(vpubA: nullable A)\l# vproA(): nullable A\l# vproA=(vproA: nullable A)\l- vpriA(): nullable A\l- vpriA=(vpriA: nullable A)\l+ vpubA2(): A\l+ vpubA2=(vpubA2: A)\l# vproA2(): A\l# vproA2=(vproA2: A)\l- vpriA2(): A\l- vpriA2=(vpriA2: A)\l- priB(a: B)\l- vpriB(): nullable B\l- vpriB=(vpriB: nullable B)\l- vpriB2(): B\l- vpriB2=(vpriB2: B)\l}"
+ label = "{A|- _vpubA: nullable A\l- _vproA: nullable A\l- _vpriA: nullable A\l- _vpubA2: A\l- _vproA2: A\l- _vpriA2: A\l- _vpriB: nullable B\l- _vpriB2: B\l|+ pubA(a: A)\l# proA(a: A)\l- priA(a: A)\l+ pubA2(): A\l# proA2(): A\l- priA2(): A\l+ vpubA(): nullable A\l+ vpubA=(vpubA: nullable A)\l# vproA(): nullable A\l# vproA=(vproA: nullable A)\l- vpriA(): nullable A\l- vpriA=(vpriA: nullable A)\l+ vpubA2(): A\l+ vpubA2=(vpubA2: A)\l# vproA2(): A\l# vproA2=(vproA2: A)\l- vpriA2(): A\l- vpriA2=(vpriA2: A)\l- priB(a: B)\l- priB2(): B\l- vpriB(): nullable B\l- vpriB=(vpriB: nullable B)\l- vpriB2(): B\l- vpriB2=(vpriB2: B)\l}"
]
Object -> A [dir=back arrowtail=open style=dashed];
Object -> Task [dir=back arrowtail=open style=dashed];
A [
- label = "{A||+ pubA(a: A)\l+ vpubA(): nullable A\l+ vpubA=(vpubA: nullable A)\l+ vpubA2(): A\l+ vpubA2=(vpubA2: A)\l}"
+ label = "{A||+ pubA(a: A)\l+ pubA2(): A\l+ vpubA(): nullable A\l+ vpubA=(vpubA: nullable A)\l+ vpubA2(): A\l+ vpubA2=(vpubA2: A)\l}"
]
Object -> A [dir=back arrowtail=open style=dashed];
-Runtime error: Cast failed. Expected `E`, got `Bool` (../lib/core/collection/array.nit:960)
+Runtime error: Cast failed. Expected `E`, got `Bool` (../lib/core/collection/array.nit:989)
NativeString
0x4e
Nit
true
true
+true
+true
+true
+
test_seq(new Array[Int], new Array[Int])
test_seq(new List[Int], new Array[Int])
test_seq(new Array[Int], new List[Int])
+test_seq(new Array[Int], new CircularArray[Int])