This PR makes all model entities Jsonable when `model_json` is imported.
Not sure if I was supposed to use the serialization process or not in this case? @xymus what do you think?
This is mostly hand picked services chosen to provide a comprehensive model representation usable by other tools like nitdoc, nitx, web tools or else.
Pull-Request: #2024
Reviewed-by: Jean Privat <jean@pryen.org>
# See the License for the specific language governing permissions and
# limitations under the License.
-import ::android::platform
+import ::android
import ::android::vibration
import asteronits
if dy > 0.0 then
# Bottom part of the joystick, turns left or right
if dx < 0.0 then
- ship.applied_rotation = -1.0
- else
ship.applied_rotation = 1.0
+ else
+ ship.applied_rotation = -1.0
end
else
# Upper part of the joystick, detect action using 45d angles
if dx < dy then
- ship.applied_rotation = -1.0
- else if dx > -dy then
ship.applied_rotation = 1.0
+ else if dx > -dy then
+ ship.applied_rotation = -1.0
else
ship.applied_thrust = 1.0
end
# Add the joystick to the UI
ui_sprites.add new Sprite(spritesheet_controls.forward,
- ui_camera.bottom_left.offset(joystick_x, -200.0, 0.0))
+ ui_camera.bottom_left.offset(joystick_x, 200.0, 0.0))
ui_sprites.add new Sprite(spritesheet_controls.left,
- ui_camera.bottom_left.offset(joystick_x-100.0, -joystick_y, 0.0))
+ ui_camera.bottom_left.offset(joystick_x-100.0, joystick_y, 0.0))
ui_sprites.add new Sprite(spritesheet_controls.right,
- ui_camera.bottom_left.offset(joystick_x+100.0, -joystick_y, 0.0))
+ ui_camera.bottom_left.offset(joystick_x+100.0, joystick_y, 0.0))
# Purely cosmetic joystick background
ui_sprites.add new Sprite(spritesheet_controls.joystick_back,
- ui_camera.bottom_left.offset(joystick_x, -joystick_y, -1.0)) # In the back
+ ui_camera.bottom_left.offset(joystick_x, joystick_y, -1.0)) # In the back
ui_sprites.add new Sprite(spritesheet_controls.joystick_down,
ui_camera.bottom_left.offset(joystick_x, 0.0, 1.0))
# Add the "open fire" button
ui_sprites.add new Sprite(spritesheet_controls.fire,
- ui_camera.bottom_right.offset(-150.0, -150.0, 0.0))
+ ui_camera.bottom_right.offset(-150.0, 150.0, 0.0))
end
end
return new HttpResponse.ok(log)
end
+ # Is `token` valid?
+ #
+ # check_token?token=a -> true | BenitluxError
+ fun check_token(token: String): HttpResponse
+ is restful do
+ var user_id = db.token_to_id(token)
+ if user_id == null then return new HttpResponse.invalid_token
+ return new HttpResponse.ok(true)
+ end
+
# Search a user
#
# search?token=b&query=a&offset=0 -> Array[UserAndFollowing] | BenitluxError
end
# ---
+# Administration
+
+# Path to the secret used to authenticate admin requests
+fun secret_path: String do return "benitlux.secret"
+
+# Services reserved to administrators
+class BenitluxAdminAction
+ super BenitluxAction
+ super RestfulAction
+
+ private fun server_secret: String do return secret_path.to_path.read_all
+
+ # Trigger sending daily menu to connected clients
+ #
+ # This should usually be called by an external cron program.
+ # send_daily_updates?secret=shared_secret -> true | BenitluxError
+ fun send_daily_updates(secret: nullable String): HttpResponse
+ is restful do
+ # Check secrets
+ var server_secret = server_secret
+ if server_secret.is_empty then
+ print_error "The admin interface needs a secret at '{secret_path}'"
+ return new HttpResponse.server_error
+ end
+
+ if server_secret != secret then
+ return new HttpResponse.invalid_token
+ end
+
+ # Load beer menu
+ var list = db.list_beers_and_rating
+ if list == null then return new HttpResponse.server_error
+
+ var msg = new DailyNotification(list)
+
+ # Broadcast updates
+ for conn in push_connections.values.to_a do
+ if not conn.closed then
+ conn.respond new HttpResponse.ok(msg)
+ conn.close
+ end
+ end
+ push_connections.clear
+
+ return new HttpResponse.ok(true)
+ end
+
+ redef fun answer(request, turi) do return new HttpResponse.bad_request
+end
+
+# ---
# Misc services
redef class Text
init ok(data: Serializable)
do
init 200
- body = data.to_json_string
+ body = data.serialize_to_json
end
# Respond with a `BenitluxError` in JSON and a code 403
do
init 403
var error = new BenitluxTokenError("Forbidden", "Invalid or outdated token.")
- body = error.to_json_string
+ body = error.serialize_to_json
end
# Respond with a `BenitluxError` in JSON and a code 400
do
init 400
var error = new BenitluxError("Bad Request", "Application error, or it needs to be updated.")
- body = error.to_json_string
+ body = error.serialize_to_json
end
# Respond with a `BenitluxError` in JSON and a code 500
do
init 500
var error = new BenitluxError("Internal Server Error", "Server error, try again later.")
- body = error.to_json_string
+ body = error.serialize_to_json
end
end
last_weekday = "date('now', 'weekday 6', '-7 day')"
else last_weekday = "date('now', '-1 day')"
- return beer_events_since(last_weekday)
+ return beer_events_since_sql(last_weekday)
end
# Build and return a `BeerEvents` for today compared to `prev_day`
# Return `null` on error
fun beer_events_since(prev_day: String): nullable BeerEvents
do
+ prev_day = prev_day.to_sql_date_string
+ return beer_events_since_sql("date({prev_day})")
+ end
+
+ # `BeerEvents` since the SQLite formatted date command `sql_date`
+ #
+ # Return `null` on error
+ private fun beer_events_since_sql(sql_date: String): nullable BeerEvents
+ do
var events = new BeerEvents
# New
var stmt = select("ROWID, name, desc FROM beers WHERE " +
"ROWID IN (SELECT beer FROM daily WHERE day=(SELECT MAX(day) FROM daily)) AND " +
- "NOT ROWID IN (SELECT beer FROM daily WHERE date(day) = date({prev_day}))")
+ "NOT ROWID IN (SELECT beer FROM daily WHERE date(day) = {sql_date})")
if stmt == null then return null
for row in stmt do events.new_beers.add row.to_beer
# Gone
stmt = select("ROWID, name, desc FROM beers WHERE " +
"NOT ROWID IN (SELECT beer FROM daily WHERE day=(SELECT MAX(day) FROM daily)) AND " +
- "ROWID IN (SELECT beer FROM daily WHERE date(day) = date({prev_day}))")
+ "ROWID IN (SELECT beer FROM daily WHERE date(day) = {sql_date})")
if stmt == null then return null
for row in stmt do events.gone_beers.add row.to_beer
# Fix
stmt = select("ROWID, name, desc FROM beers WHERE " +
"ROWID IN (SELECT beer FROM daily WHERE day=(SELECT MAX(day) FROM daily)) AND " +
- "ROWID IN (SELECT beer FROM daily WHERE date(day) = date({prev_day}))")
+ "ROWID IN (SELECT beer FROM daily WHERE date(day) = {sql_date})")
if stmt == null then return null
for row in stmt do events.fix_beers.add row.to_beer
var users = new Array[User]
end
+# Daily menu notifications
+class DailyNotification
+ serialize
+
+ # All beers on the menu today
+ var beers: Array[BeerAndRatings]
+end
+
# Server or API usage error
class BenitluxError
super Error
# Listening interface
fun iface: String do return "localhost:8080"
+# Listening interface for admin commands
+fun iface_admin: String do return "localhost:8081"
+
# Sqlite3 database
var db_path = "benitlux_sherbrooke.db"
var db = new BenitluxDB.open(db_path)
vh.routes.add new Route("/push/", new BenitluxPushAction(db))
vh.routes.add new Route(null, new BenitluxSubscriptionAction(db))
+var vh_admin = new VirtualHost(iface_admin)
+vh_admin.routes.add new Route(null, new BenitluxAdminAction(db))
+
var factory = new HttpFactory.and_libevent
factory.config.virtual_hosts.add vh
+factory.config.virtual_hosts.add vh_admin
-print "Launching server on http://{iface}/"
+print "Launching server on http://{iface}/ and http://{iface_admin}/"
factory.run
db.close
# Save the default config to pretty Json
var cc = new ClientConfig
- var json = cc.to_plain_json
- json = json.replace(",", ",\n")
+ var json = cc.serialize_to_json(plain=true, pretty=true)
json.write_to_file config_path
return cc
db.close
var response = new HttpResponse(200)
- response.body = posts.to_json_string
+ response.body = posts.serialize_to_json
return response
end
# Format not recognized
var error = new Error("Bad Request")
var response = new HttpResponse(400)
- response.body = error.to_json_string
+ response.body = error.serialize_to_json
return response
end
end
# Everyone gets the same response
var posts = list_posts(0, 16)
var response = new HttpResponse(400)
- response.body = posts.to_json_string
+ response.body = posts.serialize_to_json
for conn in push_connections do
# Complete the answer to `conn`
import tnitter_app
-import android::ui
-import android::http_request
+import android
import android::portrait
redef class LabelAuthor
module android_calculator
import calculator
-import android::ui
+import android
redef class Button
init do set_android_style(native, (text or else "?").is_int)
only be used by low-level implementations of Nit on Android.
Its usefulness will be extended in the future to customize user applications.
-## Project entry points
+## Android implementation
+
+There is two core implementation for Nit apps on Android.
+`android::nit_activity` is used by apps with standard windows and native UI controls.
+`android::game` is used by, well, games and the game frameworks `mnit` and `gamnit`.
+
+Clients don't have to select the core implementation, it is imported by other relevant modules.
+For example, a module importing `app::ui` and `android` will trigger the importation of `android::nit_activity`.
+
+## Lock app orientation
Importing `android::landscape` or `android::portrait` locks the generated
application in the specified orientation. This can be useful for games and
module android
import platform
-import native_app_glue
import dalvik
private import log
-private import assets
-
-redef class App
- redef fun init_window
- do
- super
- on_create
- on_restore_state
- on_start
- end
-
- redef fun term_window
- do
- super
- on_stop
- end
-
- # Is the application currently paused?
- var paused = true
-
- redef fun pause
- do
- paused = true
- on_pause
- super
- end
-
- redef fun resume
- do
- paused = false
- on_resume
- super
- end
-
- redef fun save_state do on_save_state
-
- redef fun lost_focus
- do
- paused = true
- super
- end
-
- redef fun gained_focus
- do
- paused = false
- super
- end
-
- redef fun destroy do on_destroy
-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.
+
+# Android services and implementation of app.nit for gamnit and mnit
+module game
+
+import platform
+import native_app_glue
+import dalvik
+private import log
+private import assets
+
+redef class App
+ redef fun init_window
+ do
+ super
+ on_create
+ on_restore_state
+ on_start
+ end
+
+ redef fun term_window
+ do
+ super
+ on_stop
+ end
+
+ # Is the application currently paused?
+ var paused = true
+
+ redef fun pause
+ do
+ paused = true
+ on_pause
+ super
+ end
+
+ redef fun resume
+ do
+ paused = false
+ on_resume
+ super
+ end
+
+ redef fun save_state do on_save_state
+
+ redef fun lost_focus
+ do
+ paused = true
+ super
+ end
+
+ redef fun gained_focus
+ do
+ paused = false
+ super
+ end
+
+ redef fun destroy do on_destroy
+end
module input_events
import mnit::input
-import android
+import android::game
in "C header" `{
#include <android/log.h>
android_manifest_activity """android:screenOrientation="sensorLandscape" """
end
-import platform
+import android
android:screenOrientation="portrait"
"""
-import platform
+import android
# See the License for the specific language governing permissions and
# limitations under the License.
-# This module is used to manipulate android sensors
-# The sensor support is implemented in android_app module, so the user can enable the type of sensor he wants to use.
-# There is an example of how you can use the android sensors in nit/examples/mnit_ballz :
+# Access Android sensors
+#
+# Sensors are to be enabled when `App` is created.
+# The following example enables all sensors.
+# The events (`SensorEvent`, `ASensorAccelerometer`, `ASensorMagneticField`...)
+# are sent to the `input` callback of `App`
#
# ~~~~nitish
-# #FIXME rewrite the example
# redef class App
-# sensors_support_enabled = true
-# accelerometer.enabled = true
-# accelerometer.eventrate = 10000
-# magnetic_field.enabled = true
-# gyroscope.enabled = true
-# light.enabled = true
-# proximity.enabled = true
+# init
+# do
+# sensors_support_enabled = true
+# accelerometer.enabled = true
+# accelerometer.eventrate = 10000
+# magnetic_field.enabled = true
+# gyroscope.enabled = true
+# light.enabled = true
+# proximity.enabled = true
+# end
# end
# ~~~~
-#
-# In this example, we enable the sensor support, then enable all types of sensors supported by the API, directly with `App` attributes
-# As a result, you get all type of SensorEvent (ASensorAccelerometer, ASensorMagneticField ...) in the `input` callback of `App`
module sensors
-import android
+import game
import mnit
in "C header" `{
import core::error
# Platform variations
-# TODO: move on the platform once qualified names are understand in the condition
import linux::audio is conditional(linux)
import android::audio is conditional(android)
import serialization
# Platform variations
-# TODO: move on the platform once qualified names are understand in the condition
import linux::data_store is conditional(linux)
import android::data_store is conditional(android)
import ios::data_store is conditional(ios)
import app_base
# Platform variations
-# TODO: move on the platform once qualified names are understand in the condition
import linux::ui is conditional(linux)
-import android::ui is conditional(android) # FIXME it should be conditional to `android::platform`
+import android::ui is conditional(android)
import ios::ui is conditional(ios)
redef class App
android_manifest """<uses-feature android:glEsVersion="0x00020000"/>"""
end
-import ::android
+import ::android::game
intrude import android::load_image
private import gamnit::egl
# Handles serialization and deserialization of objects to/from JSON
#
-# ## Nity JSON
+# ## Writing JSON with metadata
#
# `JsonSerializer` write Nit objects that subclass `Serializable` to JSON,
-# and `JsonDeserializer` can read them. They both use meta-data added to the
+# and `JsonDeserializer` can read them. They both use metadata added to the
# generated JSON to recreate the Nit instances with the exact original type.
#
# For more information on Nit serialization, see: ../serialization/README.md
#
-# ## Plain JSON
+# ## Writing plain JSON
#
# The attribute `JsonSerializer::plain_json` triggers generating plain and
# clean JSON. This format is easier to read for an human and a non-Nit program,
# but it cannot be fully deserialized. It can still be read by services from
# `json::static` and `json::dynamic`.
#
-# A shortcut to this service is provided by `Serializable::to_plain_json`.
+# A shortcut to these writing services is provided by `Serializable::serialize_to_json`.
#
# ### Usage Example
#
# var bob = new Person("Bob", 1986)
# var alice = new Person("Alice", 1978, bob)
#
-# assert bob.to_plain_json == """
-# {"name": "Bob", "year_of_birth": 1986, "next_of_kin": null}"""
+# assert bob.serialize_to_json(pretty=true, plain=true) == """
+#{
+# "name": "Bob",
+# "year_of_birth": 1986,
+# "next_of_kin": null
+#}"""
#
-# assert alice.to_plain_json == """
-# {"name": "Alice", "year_of_birth": 1978, "next_of_kin": {"name": "Bob", "year_of_birth": 1986, "next_of_kin": null}}"""
+# assert alice.serialize_to_json(pretty=true, plain=true) == """
+#{
+# "name": "Alice",
+# "year_of_birth": 1978,
+# "next_of_kin": {
+# "name": "Bob",
+# "year_of_birth": 1986,
+# "next_of_kin": null
+# }
+#}"""
# ~~~
#
# ## JSON to Nit objects
#
-# The `JsonDeserializer` support reading JSON code with minimal meta-data
+# The `JsonDeserializer` support reading JSON code with minimal metadata
# to easily create Nit object from client-side code or configuration files.
# Each JSON object must define the `__class` attribute with the corresponding
# Nit class and the expected attributes with its name in Nit followed by its value.
# var deserializer = new JsonDeserializer(json_code)
#
# var meet = deserializer.deserialize
+#
+# # Check for errors
+# assert deserializer.errors.is_empty
+#
# assert meet isa MeetupConfig
# assert meet.description == "My Awesome Meetup"
# assert meet.max_participants == null
import ::serialization::caching
private import ::serialization::engine_tools
-import static
+private import static
+private import string_parser
# Serializer of Nit objects to Json string.
class JsonSerializer
#
# If `false`, the default, serialize to support deserialization:
#
- # * Write meta-data, including the types of the serialized objects so they can
+ # * Write metadata, including the types of the serialized objects so they can
# be deserialized to their original form using `JsonDeserializer`.
# * Use references when an object has already been serialized so to not duplicate it.
# * Support cycles in references.
# * Preserve the Nit `Char` type as an object because it does not exist in JSON.
# * The generated JSON is standard and can be read by non-Nit programs.
# However, some Nit types are not represented by the simplest possible JSON representation.
- # With the added meta-data, it can be complex to read.
+ # With the added metadata, it can be complex to read.
#
# If `true`, serialize for other programs:
#
# * Nit objects are serialized for every references, so they can be duplicated.
# It is easier to read but it creates a larger output.
# * Does not support cycles, will replace the problematic references by `null`.
- # * Does not serialize the meta-data needed to deserialize the objects
+ # * Does not serialize the metadata needed to deserialize the objects
# back to regular Nit objects.
# * Keys of Nit `HashMap` are converted to their string representation using `to_s`.
var plain_json = false is writable
end
first_attribute = true
- object.serialize_to_json self
+ object.accept_json_serializer self
first_attribute = false
if plain_json then open_objects.pop
private var text: Text
# Root json object parsed from input text.
- private var root: nullable Jsonable is noinit
+ private var root: nullable Object is noinit
# Depth-first path in the serialized object tree.
- private var path = new Array[JsonObject]
+ private var path = new Array[Map[String, nullable Object]]
# Last encountered object reference id.
#
init do
var root = text.parse_json
- if root isa JsonObject then path.add(root)
+ if root isa Map[String, nullable Object] then path.add(root)
self.root = root
end
return null
end
- if object isa JsonObject then
+ if object isa Map[String, nullable Object] then
var kind = null
if object.keys.has("__kind") then
kind = object["__kind"]
# deserialized = deserializer.deserialize
# assert deserialized isa MyError
# ~~~
- protected fun class_name_heuristic(json_object: JsonObject): nullable String
+ protected fun class_name_heuristic(json_object: Map[String, nullable Object]): nullable String
do
return null
end
return res
end
- redef fun serialize_to_json(v) do v.stream.write(to_json)
+ redef fun accept_json_serializer(v) do v.stream.write(to_json)
end
redef class Serializable
- private fun serialize_to_json(v: JsonSerializer)
+
+ # Serialize `self` to JSON
+ #
+ # Set `plain = true` to generate standard JSON, without deserialization metadata.
+ # Use this option if the generated JSON will be read by other programs or humans.
+ # Use the default, `plain = false`, if the JSON is to be deserialized by a Nit program.
+ #
+ # Set `pretty = true` to generate pretty JSON for human eyes.
+ # Use the default, `pretty = false`, to generate minified JSON.
+ #
+ # This method should not be refined by subclasses,
+ # instead `accept_json_serializer` can customize the serialization of an object.
+ #
+ # See: `JsonSerializer`
+ fun serialize_to_json(plain, pretty: nullable Bool): String
+ do
+ var stream = new StringWriter
+ var serializer = new JsonSerializer(stream)
+ serializer.plain_json = plain or else false
+ serializer.pretty_json = pretty or else false
+ serializer.serialize self
+ stream.close
+ return stream.to_s
+ end
+
+ # Refinable service to customize the serialization of this class to JSON
+ #
+ # This method can be refined to customize the serialization by either
+ # writing pure JSON directly on the stream `v.stream` or
+ # by using other services of `JsonSerializer`.
+ #
+ # Most of the time, it is preferable to refine the method `core_serialize_to`
+ # which is used by all the serialization engines, not just JSON.
+ protected fun accept_json_serializer(v: JsonSerializer)
do
var id = v.cache.new_id_for(self)
v.stream.write "\{"
v.new_line_and_indent
v.stream.write "\}"
end
-
- # Serialize this object to a JSON string with metadata for deserialization
- fun to_json_string: String
- do
- var stream = new StringWriter
- var serializer = new JsonSerializer(stream)
- serializer.serialize self
- stream.close
- return stream.to_s
- end
-
- # Serialize this object to plain JSON
- #
- # This is a shortcut using `JsonSerializer::plain_json`,
- # see its documentation for more information.
- fun to_plain_json: String
- do
- var stream = new StringWriter
- var serializer = new JsonSerializer(stream)
- serializer.plain_json = true
- serializer.serialize self
- stream.close
- return stream.to_s
- end
end
redef class Int
- redef fun serialize_to_json(v) do v.stream.write(to_s)
+ redef fun accept_json_serializer(v) do v.stream.write to_s
end
redef class Float
- redef fun serialize_to_json(v) do v.stream.write(to_s)
+ redef fun accept_json_serializer(v) do v.stream.write to_s
end
redef class Bool
- redef fun serialize_to_json(v) do v.stream.write(to_s)
+ redef fun accept_json_serializer(v) do v.stream.write to_s
end
redef class Char
- redef fun serialize_to_json(v)
+ redef fun accept_json_serializer(v)
do
if v.plain_json then
- v.stream.write to_s.to_json
+ to_s.accept_json_serializer v
else
v.stream.write "\{\"__kind\": \"char\", \"__val\": "
- v.stream.write to_s.to_json
+ to_s.accept_json_serializer v
v.stream.write "\}"
end
end
end
redef class NativeString
- redef fun serialize_to_json(v) do to_s.serialize_to_json(v)
+ redef fun accept_json_serializer(v) do to_s.accept_json_serializer(v)
end
redef class Collection[E]
end
redef class SimpleCollection[E]
- redef fun serialize_to_json(v)
+ redef fun accept_json_serializer(v)
do
# Register as pseudo object
if not v.plain_json then
end
redef class Map[K, V]
- redef fun serialize_to_json(v)
+ redef fun accept_json_serializer(v)
do
# Register as pseudo object
var id = v.cache.new_id_for(self)
v.new_line_and_indent
var k = key or else "null"
- v.stream.write k.to_s.to_json
+ k.to_s.accept_json_serializer v
v.stream.write ": "
if not v.try_to_serialize(val) then
assert val != null # null would have been serialized
interface Jsonable
# Encode `self` in JSON.
#
+ # This is a recursive method which can be refined by any subclasses.
+ # To write any `Serializable` object to JSON, see `serialize_to_json`.
+ #
# SEE: `append_json`
fun to_json: String is abstract
import mnit
import mnit::opengles1
-import ::android
+import ::android::game
intrude import ::android::input_events
in "C" `{
if val == null then return null
var deserializer = new JsonDeserializer(val)
+ var obj = deserializer.deserialize
+
if deserializer.errors.not_empty then
print_error deserializer.errors.join("\n")
return null
end
- return deserializer.deserialize
+ return obj
end
end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2015-2016 Guilherme Mansur <guilhermerpmansur@gmail.com>
+#
+# 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.
+
+module native_postgres is pkgconfig("libpq")
+
+in "C header" `{
+ #include <libpq-fe.h>
+`}
+
+extern class ExecStatusType `{int`}
+ new empty `{ return PGRES_EMPTY_QUERY; `}
+ new command_ok `{ return PGRES_COMMAND_OK; `}
+ new tuples_ok `{ return PGRES_TUPLES_OK; `}
+ new copy_out `{ return PGRES_COPY_OUT; `}
+ new copy_in `{ return PGRES_COPY_IN; `}
+ new bad_response `{ return PGRES_BAD_RESPONSE; `}
+ new nonfatal_error `{ return PGRES_NONFATAL_ERROR; `}
+ new fatal_error `{ return PGRES_FATAL_ERROR; `}
+
+ fun is_ok: Bool `{return self == PGRES_TUPLES_OK || self == PGRES_COMMAND_OK; `}
+
+ redef fun to_s import NativeString.to_s `{
+ char * err = PQresStatus(self);
+ if(err == NULL) err = "";
+ return NativeString_to_s(err);
+ `}
+end
+
+extern class ConnStatusType `{int`}
+ new connection_ok `{ return CONNECTION_OK; `}
+ new connection_bad `{ return CONNECTION_BAD; `}
+
+ fun is_ok: Bool `{return self == CONNECTION_OK; `}
+end
+
+extern class PGResult `{PGresult *`}
+ # Frees the memory block associated with the result
+ fun clear `{PQclear(self); `}
+
+ # Returns the number of rows in the query result
+ fun ntuples:Int `{ return PQntuples(self); `}
+
+ # Returns the number of columns in each row of the query result
+ fun nfields:Int `{return PQnfields(self); `}
+
+ # Returns the ExecStatusType of a result
+ fun status: ExecStatusType `{ return PQresultStatus(self); `}
+
+ # Returns the field name of a given column_number
+ fun fname(column_number:Int):String import NativeString.to_s `{
+ return NativeString_to_s( PQfname(self, column_number));
+ `}
+
+ # Returns the column number associated with the column name
+ fun fnumber(column_name:String):Int import String.to_cstring `{
+ return PQfnumber(self, String_to_cstring(column_name));
+ `}
+
+ # Returns a single field value of one row of the result at row_number, column_number
+ fun value(row_number:Int, column_number:Int):String import NativeString.to_s `{
+ return NativeString_to_s(PQgetvalue(self, row_number, column_number));
+ `}
+
+ # Tests wether a field is a null value
+ fun is_null(row_number:Int, column_number: Int): Bool `{
+ return PQgetisnull(self, row_number, column_number);
+ `}
+
+end
+extern class NativePostgres `{PGconn *`}
+
+ # Connect to a new database using the conninfo string as a parameter
+ new connectdb(conninfo: String) import String.to_cstring `{
+ PGconn * self = NULL;
+ self = PQconnectdb(String_to_cstring(conninfo));
+ return self;
+ `}
+
+ # Submits a query to the server and waits for the result returns the ExecStatustype of the query
+ fun exec(query: String): PGResult import String.to_cstring `{
+ PGresult *res = PQexec(self, String_to_cstring(query));
+ return res;
+ `}
+
+ # Prepares a statement with the given parameters
+ fun prepare(stmt: String, query: String, nParams: Int):PGResult import String.to_cstring `{
+ const char * stmtName = String_to_cstring(stmt);
+ const char * queryStr = String_to_cstring(query);
+ PGresult * res = PQprepare(self, stmtName, queryStr, nParams, NULL);
+ return res;
+ `}
+
+ fun exec_prepared(stmt: String, nParams: Int, values: Array[String], pLengths: Array[Int], pFormats: Array[Int], resultFormat: Int):PGResult import String.to_cstring, Array[String].[], Array[Int].[] `{
+ const char * stmtName = String_to_cstring(stmt);
+ const char * paramValues[nParams];
+ int paramLengths[nParams];
+ int paramFormats[nParams];
+ int i;
+ for(i = 0; i < nParams; i++)
+ paramValues[i] = String_to_cstring(Array_of_String__index(values, i));
+ for(i = 0; i < nParams; i++)
+ paramLengths[i] = Array_of_Int__index(pLengths, i);
+ for(i = 0; i < nParams; i++)
+ paramFormats[i] = Array_of_Int__index(pFormats, i);
+ PGresult * res = PQexecPrepared(self, stmtName, nParams, paramValues, paramLengths, paramFormats, resultFormat);
+ return res;
+ `}
+
+ # Returns the error message of the last operation on the connection
+ fun error: String import NativeString.to_s `{
+ char * error = PQerrorMessage(self);
+ return NativeString_to_s(error);
+ `}
+
+ # Returns the status of this connection
+ fun status: ConnStatusType `{
+ return PQstatus(self);
+ `}
+
+ # Closes the connection to the server
+ fun finish `{
+ PQfinish(self);
+ `}
+
+ # Closes the connection to the server and attempts to reconnect with the previously used params
+ fun reset `{
+ PQreset(self);
+ `}
+end
--- /dev/null
+[package]
+name=postgresql
+tags=database,lib
+maintainer=Guilherme Mansur <guilhermerpmansur@gmail.com>
+license=Apache-2.0
+[upstream]
+browse=https://github.com/nitlang/nit/tree/master/lib/postgresql/
+git=https://github.com/nitlang/nit.git
+git.directory=lib/postgresql/
+homepage=http://nitlanguage.org
+issues=https://github.com/nitlang/nit/issues
\ No newline at end of file
[package]
name=rubix
tags=algo,lib
-maintainer=Lucas Bajolet<lucas.bajolet@hotmail.com>
+maintainer=Lucas Bajolet<r4pass@hotmail.com>
license=Apache-2.0
[upstream]
browse=https://github.com/nitlang/nit/tree/master/lib/rubix.nit
#include <sqlite3.h>
`}
+in "C" `{
+ // Return code of the last call to the constructor of `NativeSqlite3`
+ static int nit_sqlite_open_error = SQLITE_OK;
+`}
+
redef class Sys
# Last error raised when calling `Sqlite3::open`
- var sqlite_open_error: nullable Sqlite3Code = null
+ fun sqlite_open_error: Sqlite3Code `{ return nit_sqlite_open_error; `}
end
extern class Sqlite3Code `{int`}
private fun native_to_s: NativeString `{
#if SQLITE_VERSION_NUMBER >= 3007015
- char *err = (char *)sqlite3_errstr(self);
+ return (char *)sqlite3_errstr(self);
#else
- char *err = "sqlite3_errstr supported only by version >= 3.7.15";
+ return "sqlite3_errstr is not supported in version < 3.7.15";
#endif
- return err;
`}
end
return sqlite3_step(self);
`}
- fun column_name(i: Int) : String import NativeString.to_s `{
- const char * name = (sqlite3_column_name(self, i));
- if(name == NULL){
- name = "";
- }
- char * ret = (char *) name;
- return NativeString_to_s(ret);
+ fun column_name(i: Int): NativeString `{
+ return (char *)sqlite3_column_name(self, i);
`}
# Number of bytes in the blob or string at row `i`
extern class NativeSqlite3 `{sqlite3 *`}
# Open a connection to a database in UTF-8
- new open(filename: NativeString) import set_sys_sqlite_open_error `{
+ new open(filename: NativeString) `{
sqlite3 *self = NULL;
int err = sqlite3_open(filename, &self);
- NativeSqlite3_set_sys_sqlite_open_error(self, (void*)(long)err);
- // The previous cast is a hack, using non pointers in extern classes is not
- // yet in the spec of the FFI.
+ nit_sqlite_open_error = err;
return self;
`}
- # Utility method to set `Sys.sqlite_open_error`
- private fun set_sys_sqlite_open_error(err: Sqlite3Code) do sys.sqlite_open_error = err
-
# Has this DB been correctly opened?
#
# To know if it has been closed or interrupted, you must check for errors with `error`.
fun is_valid: Bool do return not address_is_null
- fun destroy do close
-
# Close this connection
fun close `{
#if SQLITE_VERSION_NUMBER >= 3007014
`}
# Execute a SQL statement
- fun exec(sql: String): Sqlite3Code import String.to_cstring `{
- return sqlite3_exec(self, String_to_cstring(sql), 0, 0, 0);
+ fun exec(sql: NativeString): Sqlite3Code `{
+ return sqlite3_exec(self, sql, NULL, NULL, NULL);
`}
# Prepare a SQL statement
- fun prepare(sql: String): nullable NativeStatement import String.to_cstring, NativeStatement.as nullable `{
+ fun prepare(sql: NativeString): NativeStatement `{
sqlite3_stmt *stmt;
- int res = sqlite3_prepare_v2(self, String_to_cstring(sql), -1, &stmt, 0);
+ int res = sqlite3_prepare_v2(self, sql, -1, &stmt, 0);
if (res == SQLITE_OK)
- return NativeStatement_as_nullable(stmt);
+ return stmt;
else
- return null_NativeStatement();
+ return NULL;
`}
fun last_insert_rowid: Int `{
# Prepare and return a `Statement`, return `null` on error
fun prepare(sql: Text): nullable Statement
do
- var native_stmt = native_connection.prepare(sql.to_s)
- if native_stmt == null then return null
+ var native_stmt = native_connection.prepare(sql.to_cstring)
+ if native_stmt.address_is_null then return null
var stmt = new Statement(native_stmt)
open_statements.add stmt
# Execute the `sql` statement and return `true` on success
fun execute(sql: Text): Bool
do
- var err = native_connection.exec(sql.to_s)
+ var err = native_connection.exec(sql.to_cstring)
return err.is_ok
end
do
if not native_connection.is_valid then
var err = sys.sqlite_open_error
- if err == null then return null
+ if err.is_ok then return null
return err.to_s
end
var name: String is lazy do
assert statement_closed: statement.is_open
- return statement.native_statement.column_name(index)
+ var cname = statement.native_statement.column_name(index)
+ assert not cname.address_is_null
+ return cname.to_s
end
# Get the value of this entry according to its Sqlite type
redef universal Float super Sqlite3Data end
-redef class String
- super Sqlite3Data
+redef class String super Sqlite3Data end
+
+redef class Text
# Return `self` between `'`s, escaping `\` and `'`
#
do
return "'{self.replace('\\', "\\\\").replace('\'', "''")}'"
end
+
+ # Format the date represented by `self` into an escaped string for SQLite
+ #
+ # `self` must be composed of 1 to 3 integers separated by '-'.
+ # An incompatible format will result in an invalid date string.
+ #
+ # assert "2016-5-1".to_sql_date_string == "'2016-05-01'"
+ # assert "2016".to_sql_date_string == "'2016-01-01'"
+ fun to_sql_date_string: String
+ do
+ var parts = self.split("-")
+ for i in [parts.length .. 3[ do parts[i] = "1"
+
+ var year = parts[0].justify(4, 1.0, '0')
+ var month = parts[1].justify(2, 1.0, '0')
+ var day = parts[2].justify(2, 1.0, '0')
+ return "{year}-{month}-{day}".to_sql_string
+ end
end
# A Sqlite3 blob
$ nitunit foo.md
+When testing, the environment variable `NIT_TESTING` is set to `true`.
+This flag can be used by libraries and program to prevent (or limit) the execution of dangerous pieces of code.
+
+~~~~~
+# NIT_TESTING is automatically set.
+#
+# assert "NIT_TESTING".environ == "true"
+~~~~
+
## Working with `TestSuites`
TestSuites are Nit files that define a set of TestCases for a particular module.
# Number of line of code by package
var loc = new Counter[MPackage]
+ # Number of errors
+ var errors = new Counter[MPackage]
+
+ # Number of warnings and advices
+ var warnings = new Counter[MPackage]
+
+ # Documentation score (between 0 and 100)
+ var documentation_score = new Counter[MPackage]
+
# Number of commits by package
var commits = new Counter[MPackage]
var mclasses = 0
var mmethods = 0
var loc = 0
+ var errors = 0
+ var warnings = 0
+ # The documentation value of each entity is ad hoc.
+ var entity_score = 0.0
+ var doc_score = 0.0
for g in mpackage.mgroups do
mmodules += g.mmodules.length
+ entity_score += 1.0
+ if g.mdoc != null then doc_score += 1.0
for m in g.mmodules do
+ var source = m.location.file
+ if source != null then
+ for msg in source.messages do
+ if msg.level == 2 then
+ errors += 1
+ else
+ warnings += 1
+ end
+ end
+ end
var am = modelbuilder.mmodule2node(m)
if am != null then
var file = am.location.file
loc += file.line_starts.length - 1
end
end
+ entity_score += 1.0
+ if m.mdoc != null then doc_score += 1.0
for cd in m.mclassdefs do
+ var s = 0.2
+ if not cd.is_intro then s /= 10.0
+ if not cd.mclass.visibility <= private_visibility then s /= 10.0
+ entity_score += s
+ if cd.mdoc != null then doc_score += s
mclasses += 1
for pd in cd.mpropdefs do
+ s = 0.1
+ if not pd.is_intro then s /= 10.0
+ if not pd.mproperty.visibility <= private_visibility then s /= 10.0
+ entity_score += s
+ if pd.mdoc != null then doc_score += s
if not pd isa MMethodDef then continue
mmethods += 1
end
self.mclasses[mpackage] = mclasses
self.mmethods[mpackage] = mmethods
self.loc[mpackage] = loc
+ self.errors[mpackage] = errors
+ self.warnings[mpackage] = warnings
+ var documentation_score = (100.0 * doc_score / entity_score).to_i
+ self.documentation_score[mpackage] = documentation_score
#score += mmodules.score
score += mclasses.score
score += mmethods.score
score += loc.score
+ score += documentation_score.score
self.score[mpackage] = score.to_i
end
end
res.add "</ul>\n"
+ res.add "<h3>Quality</h3>\n<ul class=\"box\">\n"
+ var errors = errors[mpackage]
+ if errors > 0 then
+ res.add "<li>{errors} errors</li>\n"
+ end
+ res.add "<li>{warnings[mpackage]} warnings</li>\n"
+ res.add "<li>{documentation_score[mpackage]}% documented</li>\n"
+ res.add "</ul>\n"
+
res.add "<h3>Tags</h3>\n"
var ts2 = new Array[String]
for t in mpackage.tags do
res.add "<th data-field=\"met\" data-sortable=\"true\">methods</th>\n"
res.add "<th data-field=\"loc\" data-sortable=\"true\">lines</th>\n"
res.add "<th data-field=\"score\" data-sortable=\"true\">score</th>\n"
+ res.add "<th data-field=\"errors\" data-sortable=\"true\">errors</th>\n"
+ res.add "<th data-field=\"warnings\" data-sortable=\"true\">warnings</th>\n"
+ res.add "<th data-field=\"doc\" data-sortable=\"true\">doc</th>\n"
res.add "</tr></thead>"
for p in mpackages do
res.add "<tr>"
res.add "<td>{mmethods[p]}</td>"
res.add "<td>{loc[p]}</td>"
res.add "<td>{score[p]}</td>"
+ res.add "<td>{errors[p]}</td>"
+ res.add "<td>{warnings[p]}</td>"
+ res.add "<td>{documentation_score[p]}</td>"
res.add "</tr>\n"
end
res.add "</table>\n"
var mmodules = modelbuilder.parse_full(args)
modelbuilder.run_phases
-if opt_full.value then mmodules = model.mmodules
+if opt_full.value then mmodules = modelbuilder.parsed_modules
var dir = opt_dir.value
if dir != null then
exit(0)
end
+"NIT_TESTING".setenv("true")
+
var page = new HTMLTag("testsuites")
if toolcontext.opt_full.value then mmodules = model.mmodules
# * enclose identifiers, keywords and pieces of code with back-quotes.
var text: String
+ # The severity level
+ #
+ # * 0 is advices (see `ToolContext::advice`)
+ # * 1 is warnings (see `ToolContext::warning`)
+ # * 2 is errors (see `ToolContext::error`)
+ var level: Int
+
# Comparisons are made on message locations.
redef fun <(other: OTHER): Bool do
if location == null then return true
messages = ms
end
ms.add m
+ var s = file
+ if s != null then s.messages.add m
end
end
+redef class SourceFile
+ # Errors and warnings associated to the whole source.
+ var messages = new Array[Message]
+end
+
# Global context for tools
class ToolContext
# Number of errors
# Return the message (to add information)
fun error(l: nullable Location, s: String): Message
do
- var m = new Message(l,null,s)
+ var m = new Message(l, null, s, 2)
if messages.has(m) then return m
if l != null then l.add_message m
messages.add m
# Return the message (to add information) or null if the warning is disabled
fun warning(l: nullable Location, tag: String, text: String): nullable Message
do
- if opt_warning.value.has("no-{tag}") then return null
- if not opt_warning.value.has(tag) and opt_warn.value == 0 then return null
if is_warning_blacklisted(l, tag) then return null
- var m = new Message(l, tag, text)
+ var m = new Message(l, tag, text, 1)
if messages.has(m) then return null
if l != null then l.add_message m
+ if opt_warning.value.has("no-{tag}") then return null
+ if not opt_warning.value.has(tag) and opt_warn.value == 0 then return null
messages.add m
warning_count = warning_count + 1
if opt_stop_on_first_error.value then check_errors
# Return the message (to add information) or null if the warning is disabled
fun advice(l: nullable Location, tag: String, text: String): nullable Message
do
- if opt_warning.value.has("no-{tag}") then return null
- if not opt_warning.value.has(tag) and opt_warn.value <= 1 then return null
if is_warning_blacklisted(l, tag) then return null
- var m = new Message(l, tag, text)
+ var m = new Message(l, tag, text, 0)
if messages.has(m) then return null
if l != null then l.add_message m
+ if opt_warning.value.has("no-{tag}") then return null
+ if not opt_warning.value.has(tag) and opt_warn.value <= 1 then return null
messages.add m
warning_count = warning_count + 1
if opt_stop_on_first_error.value then check_errors
<li><a href="https://github.com/nitlang/nit/tree/master/tests/test_prog">https://github.com/nitlang/nit/tree/master/tests/test_prog</a></li>
<li><tt>https://github.com/nitlang/nit.git</tt></li>
</ul>
+<h3>Quality</h3>
+<ul class="box">
+<li>28 warnings</li>
+<li>93% documented</li>
+</ul>
<h3>Tags</h3>
<a href="../index.html#tag_test">test</a>, <a href="../index.html#tag_game">game</a><h3>Requirements</h3>
none<h3>Clients</h3>
</span></span><span class="line" id="L33">
</span><span class="nc_cdef foldable" id="base_simple3$B"><span class="line" id="L34"><span class="nc_k">class</span> <span class="nc_t">B</span>
</span><span class="nc_pdef foldable" id="base_simple3$B$_val"><a id="base_simple3$B$val"></a><a id="base_simple3$B$val="></a><span class="line" id="L35"> <span class="nc_k">var</span> <span class="nc_def nc_i">val</span><span>:</span> <span class="nc_t">Int</span>
-</span></span><span class="nc_pdef foldable" id="base_simple3$B$init"><span class="line" id="L36"> <span class="nc_k">init</span><span>(</span><span class="nc_v nc_i">v</span><span>:</span> <span class="nc_t">Int</span><span>)</span>
+</span></span><span class="nc_pdef foldable" id="base_simple3$B$init"><span class="line" id="L36"> <span class="nc_k popupable" style="border-bottom: solid 2px red" title="Messages" data-content="<div><div class="dropdown"> <a data-toggle="dropdown" href="#"><b>1 message(s)</b> <span class="caret"></span></a><ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"><li>Warning: init with signature in base_simple3$B</li></ul></div></div>" data-toggle="popover">init</span><span>(</span><span class="nc_v nc_i">v</span><span>:</span> <span class="nc_t">Int</span><span>)</span>
</span><span class="line" id="L37"> <span class="nc_k">do</span>
</span><span class="line" id="L38"> <span class="nc_l">7</span><span>.</span><span class="nc_i">output</span>
</span><span class="line" id="L39"> <span class="nc_k">self</span><span>.</span><span class="nc_i">val</span> <span>=</span> <span class="nc_v nc_i">v</span>
<h1>base_simple3$B$val=</h1>
<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3$B$_val"><a id="base_simple3$B$val"></a><a id="base_simple3$B$val="></a><span class="line" id="L35"> <span class="nc_k">var</span> <span class="nc_def nc_i">val</span><span>:</span> <span class="nc_t">Int</span></span></span></span></code></pre>
<h1>base_simple3$B$init</h1>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3$B$init"><span class="line" id="L36"> <span class="nc_k">init</span><span>(</span><span class="nc_v nc_i">v</span><span>:</span> <span class="nc_t">Int</span><span>)</span>
+<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3$B$init"><span class="line" id="L36"> <span class="nc_k popupable" style="border-bottom: solid 2px red" title="Messages" data-content="<div><div class="dropdown"> <a data-toggle="dropdown" href="#"><b>1 message(s)</b> <span class="caret"></span></a><ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"><li>Warning: init with signature in base_simple3$B</li></ul></div></div>" data-toggle="popover">init</span><span>(</span><span class="nc_v nc_i">v</span><span>:</span> <span class="nc_t">Int</span><span>)</span>
</span><span class="line" id="L37"> <span class="nc_k">do</span>
</span><span class="line" id="L38"> <span class="nc_l">7</span><span>.</span><span class="nc_i">output</span>
</span><span class="line" id="L39"> <span class="nc_k">self</span><span>.</span><span class="nc_i">val</span> <span>=</span> <span class="nc_v nc_i">v</span>
</span></span><span class="line" id="L33">
</span><span class="nc_cdef foldable" id="base_simple3$B"><span class="line" id="L34"><span class="nc_k">class</span> <span class="nc_t">B</span>
</span><span class="nc_pdef foldable" id="base_simple3$B$_val"><a id="base_simple3$B$val"></a><a id="base_simple3$B$val="></a><span class="line" id="L35"> <span class="nc_k">var</span> <span class="nc_def nc_i">val</span><span>:</span> <span class="nc_t">Int</span>
-</span></span><span class="nc_pdef foldable" id="base_simple3$B$init"><span class="line" id="L36"> <span class="nc_k">init</span><span>(</span><span class="nc_v nc_i">v</span><span>:</span> <span class="nc_t">Int</span><span>)</span>
+</span></span><span class="nc_pdef foldable" id="base_simple3$B$init"><span class="line" id="L36"> <span class="nc_k popupable" style="border-bottom: solid 2px red" title="Messages" data-content="<div><div class="dropdown"> <a data-toggle="dropdown" href="#"><b>1 message(s)</b> <span class="caret"></span></a><ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"><li>Warning: init with signature in base_simple3$B</li></ul></div></div>" data-toggle="popover">init</span><span>(</span><span class="nc_v nc_i">v</span><span>:</span> <span class="nc_t">Int</span><span>)</span>
</span><span class="line" id="L37"> <span class="nc_k">do</span>
</span><span class="line" id="L38"> <span class="nc_l">7</span><span>.</span><span class="nc_i">output</span>
</span><span class="line" id="L39"> <span class="nc_k">self</span><span>.</span><span class="nc_i">val</span> <span>=</span> <span class="nc_v nc_i">v</span>
# Nit: <MyClass i:123 s:hello f:123.456 a:[one, two] o:<null>>
# JSON: not valid json
-# Errors: 'Error Parsing JSON: [1:1-1:2] Unexpected character 'n'; is acceptable instead: value'
+# Errors: 'Parsing error at line 1, position 1: Error: bad JSON entity'
# Nit: null
--- /dev/null
+aname class sex
+Whale mammal 1
+Snake reptile 0
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Guilherme Mansur <guilhermerpmansur@gmail.com>
+#
+# 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.
+
+module test_postgres_native
+
+import postgresql::native_postgres
+
+var db = new NativePostgres.connectdb("dbname=postgres")
+assert postgres_open: db.status.is_ok else print_error db.error
+
+var result = db.exec("CREATE TABLE IF NOT EXISTS animals (aname TEXT PRIMARY KEY, class TEXT NOT NULL, sex INTEGER)")
+assert postgres_create_table: result.status.is_ok else print_error db.error
+
+result = db.exec("INSERT INTO animals VALUES('Whale', 'mammal', 1)")
+assert postgres_insert_1: result.status.is_ok else print_error db.error
+
+result = db.exec("INSERT INTO animals VALUES('Snake', 'reptile', 0)")
+assert postgres_insert_2: result.status.is_ok else print_error db.error
+
+result = db.exec("SELECT * FROM animals")
+assert postgres_select: result.status.is_ok else print_error db.error
+
+assert postgres_ntuples: result.ntuples == 2 else print_error db.error
+assert postgres_nfields: result.nfields == 3 else print_error db.error
+assert postgres_fname: result.fname(0) == "aname" else print_error db.error
+assert postgres_isnull: result.is_null(0,0) == false else print_error db.error
+assert postgres_value: result.value(0,0) == "Whale" else print_error db.error
+
+var cols: Int = result.nfields
+var rows: Int = result.ntuples
+var fields: String = ""
+for c in [0..cols[ do fields += result.fname(c) + " "
+print fields
+for i in [0..rows[ do
+ fields = ""
+ for j in [0..cols[ do fields += result.value(i, j) + " "
+ print fields
+end
+
+result = db.exec("DELETE FROM animals WHERE aname = 'Lioness'")
+assert postgres_delete_1: result.status.is_ok else print_error db.error
+
+result = db.exec("DELETE FROM animals WHERE aname = 'Snake'")
+assert postgres_delete_2: result.status.is_ok else print_error db.error
+
+result = db.prepare("PREPARED_INSERT", "INSERT INTO animals(aname, class, sex) VALUES ($1, $2, $3)", 3)
+assert postgres_prepare: result.status.is_ok else print_error db.error
+
+result = db.exec("DELETE FROM animals WHERE aname = 'Frog'")
+assert postgres_delete_3: result.status.is_ok else print_error db.error
+
+var values = ["Frog", "Anphibian", "1"]
+var lengths = [values[0].length, values[1].length, values[2].length]
+var formats = [0,0,0]
+result = db.exec_prepared("PREPARED_INSERT", 3, values, lengths, formats,0)
+assert postgres_exec_prepared: result.status.is_ok else print_error db.error
+
+result = db.exec("DROP TABLE animals")
+assert postgres_drop_table: result.status.is_ok else print_error db.error
+db.finish
var db = new NativeSqlite3.open(filename.to_cstring)
assert sqlite_open: db.error.is_ok
-db.exec(create_req)
+db.exec(create_req.to_cstring)
assert sqlite_create_table: db.error.is_ok
-db.exec(insert_req_1)
+db.exec(insert_req_1.to_cstring)
assert sqlite_insert_1: db.error.is_ok
-db.exec(insert_req_2)
+db.exec(insert_req_2.to_cstring)
assert sqlite_insert_2: db.error.is_ok
-var stmt = db.prepare(select_req)
+var stmt = db.prepare(select_req.to_cstring)
assert sqlite_select: db.error.is_ok
-if stmt == null then
+if stmt.address_is_null then
print "Prepared failed got: {db.error.to_s}"
abort
end
db = new NativeSqlite3.open(filename.to_cstring)
assert sqlite_reopen: db.error.is_ok
-stmt = db.prepare(select_req)
+stmt = db.prepare(select_req.to_cstring)
assert sqlite_reselect: db.error.is_ok
-assert stmt != null
+assert not stmt.address_is_null
stmt.step
assert sqlite_column_0_0_reopened: stmt.column_text(0).to_s == "Bob"