Giter Site home page Giter Site logo

norm's Introduction

Welcome to Norm!

Norm is an object-driven, framework-agnostic ORM for Nim that supports SQLite and PostgreSQL.

  • Documentation (built with nimibook)
  • API index
  • norman: scaffolder and migration manager for Norm
  • shopapp: a proof-of-concept for a webapp created with Karax for frontend, Jester for API server, Norm for ORM, and Norman for migration management

Installation

Install Norm with Nimble:

$ nimble install -y norm

Add Norm to your .nimble file:

requires "norm"

Norm requires Nimble 0.14.0 sonmake sure you have the latest Nimble installed by running:

$ nimble install -y nimble

Contributing

Any contributions are welcome: pull requests, code reviews, documentation improvements, bug reports, and feature requests.

  • See the issues on GitHub.

  • Run the tests before and after you change the code.

    The recommended way to run the tests is via nimble commands:

    $ nimble startContainers                                # Starts docker containers needed for testing
    $ nimble startContainers sudo                           # Starts docker containers using sudo
    
    $ nimble allTests                                       # run all test suites
    $ nimble singleTest tests/common/tmodel.nim             # run a single/list of test suite/s
    
    $ nimble stopContainers                                 # Stops and shuts down docker-containers
    $ nimble startContainers sudo                           # Stops docker container using sudo
    
  • Use camelCase instead of snake_case.

  • New procs must have a documentation comment. If you modify an existing proc, update the comment.

  • Apart from the code that implements a feature or fixes a bug, PRs are required to ship necessary tests and a changelog updates.

❤ Contributors ❤

Norm would not be where it is today without the efforts of these fine folks: https://github.com/moigagoo/norm/graphs/contributors.

norm's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

norm's Issues

Supports more than one FK per object

For a table with more than one FK, norm currently uses this syntax:

CREATE TABLE table (
	id INTEGER PRIMARY KEY,
	name TEXT NOT NULL,
	fk1 integer, FOREIGN KEY (fk1) REFERENCES table1 (id),
	fk2 integer, FOREIGN KEY (fk2) REFERENCES table2 (id)
)

Which doesn't work with SQLite. Instead this should be used:

CREATE TABLE table (
	id INTEGER PRIMARY KEY,
	name TEXT NOT NULL,
	fk1 integer REFERENCES table1 (id),
	fk2 integer REFERENCES table2 (id)
)

Wrap body within withDb and withCustomDb in migration

Basically, exec BEGIN before body, exec COMMIT after it, and exec ROLLBACK in the except block.

This is easy to implement but is currently impossible due to the fact that you can call withDb in parsers and formatter which produces nested transactions, and those are not supported by either PostgreSQL or SQLite. This would not be an issue if we had proper fk handling because this would eliminate the need for the ugly code like this:

proc getBookById(id: string): Book = withDb(Book.getOne parseInt(id))

So, we first need to somehow handle foreign keys in a way that would not require manual withDb calls. Then, it would probably be a good idea to forbid withDb calls in parsers and formatters at all. Then, it will be trivial to wrap withDb in transaction.

feedback request for refactoring of macros.hasCustomPragma

I am currently refactoring macros.hasCustomPragma. Here is the PR, but when I try to compile tsqlite with that refactoring enabled I get the following error. Can you tell me what could have gone wrong?

tests/tsqlite.nim(49, 7) template/generic instantiation of `suite` from here
tests/tsqlite.nim(79, 8) template/generic instantiation of `test` from here
tests/tsqlite.nim(80, 5) template/generic instantiation of `withDb` from here
tests/tsqlite.nim(86, 39) Error: type mismatch: got <typeof(nil)>
but expected one of: 
proc dbValue(v: DbValue): DbValue
  first type mismatch at position: 1
  required type for v: DbValue
  but expression 'nil' is of type: typeof(nil)
proc dbValue(v: int | int32 | int64 | uint): DbValue
  first type mismatch at position: 1
  required type for v: int or int32 or int64 or uint
  but expression 'nil' is of type: typeof(nil)
proc dbValue(v: float): DbValue
  first type mismatch at position: 1
  required type for v: float
  but expression 'nil' is of type: typeof(nil)
proc dbValue(v: string): DbValue
  first type mismatch at position: 1
  required type for v: string
  but expression 'nil' is of type: typeof(nil)
proc dbValue(v: DbBlob): DbValue
  first type mismatch at position: 1
  required type for v: DbBlob
  but expression 'nil' is of type: typeof(nil)
proc dbValue(v: DbNull): DbValue
  first type mismatch at position: 1
  required type for v: DbNull
  but expression 'nil' is of type: typeof(nil)
proc dbValue(v: DateTime): DbValue
  first type mismatch at position: 1
  required type for v: DateTime
  but expression 'nil' is of type: typeof(nil)
proc dbValue[T](v: Option[T]): DbValue
  first type mismatch at position: 1
  required type for v: Option[dbValue.T]
  but expression 'nil' is of type: typeof(nil)
proc dbValue(v: bool): DbValue
  first type mismatch at position: 1
  required type for v: bool
  but expression 'nil' is of type: typeof(nil)

Read DB config from env vars

Common practice it to store your DB config separate from the code, in the env vars.

To make Norm more suitable for real-world applications and to make it easier to create tools for it (like Norman), we should add ready-to-use tools to allow reading DB config from env vars.

dbValue for DateTime not valid in querying

Minimal example:

import times
import norm/[model, sqlite]
import sugar

type
  TestTime = ref object of Model
    time: DateTime

proc newTest(): TestTime = TestTime(time: now())

when isMainModule:
  var
    db = getDb()
    t = newTest().dup:
      db.select("TestTime.time <= ?", ?now())
  db.close()

Using the latest version installed by nimble (2.1.1).

Attempting to compile gives Error: type mismatch: got <DateTime>, listing all the other types defined in norm/private/sqlite/dbtypes. Attempting to define my own conversion procs (i.e. copying the DateTime func from that file) gives an ambiguous call error:

Error: ambiguous call; both dbtypes.dbValue(val: DateTime) [declared in ~/.nimble/pkgs/norm-2.1.1/norm/private/sqlite/dbtypes.nim(41, 6)] and database.dbValue(val: DateTime) [declared in ~/dev/test/src/test.nim(5, 8)] match for: (DateTime)

I haven't had this issue declaring my own conversion methods for other types (e.g., Time). The issue doesn't crop up for inserting into the database or creating tables, just in select that I've found (so far).

Defaults for new columns during migration

Have you considered using a macro? Consider this, maybe it will help:

import macros, times, options

macro typToDefault*(typ: string): untyped =
  let i = parseExpr(typ.strVal)
  quote do: default(`i`)

echo typToDefault("int")         # 0
echo typToDefault("float")       # 0.0
echo typToDefault("DateTime")    # 0001-00-00T00:00:00+00:00
echo typToDefault("Option[int]") # None[int]

Note: parseExpr was used here because ident("Option[int]") is invalid.

Support collections (seq/set)

This is a really awesome library! Thank you for putting it together.

One immediate thing I've tried is an object that contains a set[EnumType], would love it if this worked out of the box.

possible change to the db macro setup

I'm not convinced what I'm about to suggest is a good idea. But, I'd like to suggest it and show how/why it could work.

Rather than use the db macro, use a similar table macro that is generic to all objects. Then, use a passed variable for the actual db references.

Example:

table:
  type                           
    User = object
      age: int 
      name {.
        formatIt: capitalize(it)  # Guarantee that ``name`` is stored in DB capitalized.
      .}: string

db = database("petshop.db", "", "", ""):

withDb:                         
  createTables(db, force=true)

  var bob = User(                 # Create a ``User`` instance as you normally would.
    name: "bob",                  # We are inserting a null!
    age: 23
  )
  bob.insert(db)

Why do such a thing? This biggest one is that allows the same object to be used across multiple databases; even databases of different types. For example:

table():
  type                           
    User = object
      age: int 
      name {.
        formatIt: capitalize(it)  # Guarantee that ``name`` is stored in DB capitalized.
      .}: string

dbA = database("petshop.db", "", "", ""):
dbB = database("larry.db", "", "", ""):

withDb:                         
  createTables(dbA, force=true)
  createTables(dbB, force=true)

  var bob = User(                 # Create a ``User`` instance as you normally would.
    name: "bob",                  # We are inserting a null!
    age: 23
  )
  bob.insert(dbA)
  bob.insert(dbB)

Or, it could possibly be referenced in reverse:

  dbA.insert(bob)
  dbB.insert(bob)

though, that is less oriented to the object. I prefer the earlier.

Another minor benefit of this is then the parameters to table could be open-ended in nature:

table(alias="customer_idents"):
  type                           
    User = object
      age: int 
      name {.
        formatIt: capitalize(it)  # Guarantee that ``name`` is stored in DB capitalized.
      .}: string

The big downside, of course, is that this is a VERY BIG change. Nothing subtle about it. :)

DBValue do not convert to Field type Positive correctly.

continue with #27
Thanks for your response, The code in #27 can be compiled.

continue to add code as follows:

withDb:
  let bobs = User.getMany(
    100,
    cond="name LIKE 'Bob%' ORDER BY age"
  )

  dump bobs

got build errors:

d:\sources\nim\main.nim(31) main
C:\Users\user\.nimble\pkgs\norm-1.0.16\norm\sqlite.nim(167) getMany
C:\Users\user\.nimble\pkgs\norm-1.0.16\norm\sqlite\rowutils.nim(105) getMany
D:\scoop\apps\nim\current\lib\system\fatal.nim(48) sysFatal
Error: unhandled exception: i is not accessible [FieldError]
Error: execution of an external program failed: 'd:\sources\nim\main.exe '

The problem could be line 105:
obj.dot(field) = row[i].i.int
in C:\Users\hxk.nimble\pkgs\norm-1.0.16\norm\sqlite\rowutils.nim,
the type of field age in User is Positive, but row[i] DBValue has no i field.

'None' Optional models are excluded from queries, including selects

Using norm version 2.1.1.

Minimal reproduction:

import logging, options
import norm/[model, sqlite]

addHandler(newConsoleLogger())

type
  A = ref object of Model
    a: int
  B = ref object of Model
    a: Option[A]
    b: int

func newB(): B = B(a: none A, b: 0)

when isMainModule:
  withDb:
    db.createTables(newB())

I'd expect it to create tables for both A and B here, as B depends on A, but the output I get is only (formatted for readability):

DEBUG CREATE TABLE IF NOT EXISTS "B"(
  a INTEGER,
  b INTEGER NOT NULL,
  id INTEGER NOT NULL PRIMARY KEY,
  FOREIGN KEY(a) REFERENCES "A"(id)
)

The same behavior is observed with querying, updating, etc:

discard newB().dup:
  db.select("B.b > ?", 0)

results in not joining onto the A table:

DEBUG SELECT "B".b, "B".id FROM "B"  WHERE B.b > ? <- [0]

For comparison, changing B.a to be just an A and adjusting newB() accordingly results in the following output instead, matching the README:

DEBUG CREATE TABLE IF NOT EXISTS "A"(
  a INTEGER NOT NULL,
  id INTEGER NOT NULL PRIMARY KEY
)
DEBUG CREATE TABLE IF NOT EXISTS "B"(
  a INTEGER NOT NULL,
  b INTEGER NOT NULL,
  id INTEGER NOT NULL PRIMARY KEY,
  FOREIGN KEY(a) REFERENCES "A"(id)
)
DEBUG SELECT "A".a, "A".id, "B".b, "B".id FROM "B" JOIN "A" ON "B".a = "A".id WHERE B.b > ? <- [0]

Deprecate default and notNull pragmas

default has been deprecated in #33 because default values are now set for all NOT NULL columns automatically. The only case where this pragma could be useful is if you have a read-only column. To cover this case, specify DEFAULT in dbType.

notNull pragma makes no sense after Postres was migrated to ndb: all columns are NOT NULL be default except for Option type.

  • default
  • notNull

safe filtering

Are there any plans on creating some kind of prepared where statements to prevent sql injection when using user input?

Please revert my commits

Due to several issues I have had in the Nim community, as well as general behavior I've noticed from browsing IRC logs, I no longer wish to be associated with the Nim community or Nim projects in any way, including through the use of my code.

As such, I'd like to request that you revert the following commits: dbf36210459ec638990a28270e0bca155fe62166, d5241c186f8ee970f9f1fdd0edbcd56d3d251a54, 53684a2081cf1e3a68b9bea7773440451f864430, c72f4b2b8810c9aa4397ac288809d0f9c11a1566, ccc704ff862ec7c762385c5751a59dfbcbb42920, c7f96c1c5df7a4d36e547149869b7b9a9ecf109c, 8326db6cd191827b6ff731028c5a5b237357b0f5, a392d88a998ccc03454a27f4a595147abb62df39, fa25294b0ac42f4f9682936adf25bf66be6afdac.

This is by no means a legal threat, simply a request. I would prefer my code not be used, but I can't force you to stop using it.

Apologies for any disruption this may cause.

Crazy idea #1: adding support for MongoDB

A type object would be the perfect way for a MongoDB document database to store and retrieve objects.

Essentially, adding optional support for the nimongo library in addition to the sqlite and postgres databases.

A side effect is that the postgres will also start supporting:

arrays - https://www.postgresql.org/docs/9.5/arrays.html
composites - https://www.postgresql.org/docs/9.5/rowtypes.html

since MongoDB pretty much requires that also. I'm thinking composites would be done with references to other type objects. Such as:

composite:
  type
    Address = object
      Street: string
      City: string
      State: string

db("petshop.db", "", "", ""):
  type       
    User = object
      age: int
      name: string
      home: Address

I'd be happy to write it and do a PR. But wanted to see if you were interested in it for the library.

Is there anyway to search for specific object fields

I'm having an object like this:

type
  User* = object
    name {.unique.}: string
    password: string

Later on I'd want to get the object with name = "username" and there doesn't seem to be any integrated method for this. I'm I stuck with executing SQL manually via dbConn?

Bulk insert / Batching

Enhancement to add bulk editing and batching
bulk insert statement

type
    User2 = object
       age: Positive

withDb:
 let users: seq[User2]
 users.add(User(age=2))
 users.and(User(age=2))
 users.add(User(age=2))
 users.save()

which would execute

INSERT INTO user2 (age) VALUES (?, ?), 
(2),
(2),
(2);

Batching
*could just have this and map over prepare() and then db.save() instead of the bulk example above

type
    User2 = object
       age: Positive
type
    Book = object
       age: Positive

withDB:
  var user = User2(age: 1)
  user.prepare()
  var book = Book(age: 1)
  book.prepare()
  db.save()

which would execute one sql

INSERT INTO user2 (age) VALUES (?, ?), 
(1),
INSERT INTO book (age) VALUES (?, ?), 
(1),

Cannot set fk to int64 when returning the model from a helper proc

When I define the foreign key as int64 and want to write a helper proc that will return the model/object, I'll get the following error. That's because in sqlites rowutils the to template does not handle int64 types. I can compile everything successfully when i comment out my helper proc or change the foreign key type to int.

normtest.nim(46, 1) template/generic instantiation of `withDb` from here
normtest.nim(49, 24) template/generic instantiation of `getOne` from here
.nimble/pkgs/norm-1.0.17/norm/sqlite.nim(137, 15) template/generic instantiation of `getOne` from here
.nimble/pkgs/norm-1.0.17/norm/sqlite.nim(132, 17) template/generic instantiation of `to` from here
.nimble/pkgs/norm-1.0.17/norm/sqlite/rowutils.nim(129, 9) Error: unreachable statement after 'return' statement or '{.noReturn.}' proc

Crazy idea #2: support for CRUD html forms

Expanding on the use of type object as a central reference point: adding support for html generation of CRUD forms for a web sites.

It would be optional, the user would have to import norm/forms for it to compile and work.

The idea is that, when writing a website that uses the database, the web server more easily communicates with database object. For example, with your sample web app, instead of:

router owners:
  post "/":
    try:
      var owner = Owner(firstName: @"firstName", lastName: @"lastName",
                        birthDate: @"birthDate".parse("yyyy-MM-dd", utc()))

      withDb:
        owner.insert()

      resp %owner

    except:
      resp Http400

You could do something like:

router owners:
  post "/":
    try:
      var owner = Owner().parseRequest(request)

      withDb:
        owner.insert()

      resp %owner

    except:
      resp Http400

In the other direction, there could be a generateHTML function that reads the object instance into a string containing the details of a form with class tags for styling by CSS.

Happy to start writing it as I'd use it on a web site. But wanted to get your feedback first.

This could also be a separate library that depended on norm rather than an expansion of norm. The benefit of expanding norm is that the {. .} pragmas could be tweaked to also support html display and entry conditions.

Select query selects same row twice for fields of same model type

Opening a per this comment on the forums.

If a model has two fields of the same type that reference a different model, both fields will be populated with the same data. This gist shows the behavior, and generates the following SQL:

SELECT "Foo".str, "Foo".val, "Foo".id, "Foo".str, "Foo".val, "Foo".id, "Bar".id FROM "Bar" JOIN "Foo" ON "Bar".foo1 = "Foo".id WHERE Bar.id = ? <- [1]

[FR] Support for BLOB field

First, I must say I'm loving this library !

Is there a possitibility for supporting BLOB type with SQLite ? It would ease the use of Nim for computing / storing large set of datas or image; for instance a Tensor (as defined in Arraymancer) could be converted to a BLOB and inserted into a database.

[FR] mariadb support

I'm learning nim and i'm used to using mariadb for my laravel apps, so mariadb support would be nice from my perspective. Is this something that might be added in the future, or something norm devs are decided/set against?
thanks

unreachable statement after 'return'

Error:
norm-1.0.17\norm\sqlite\rowutils.nim(129, 9) Error: unreachable statement after 'return' statement or '{.noReturn.}' proc

Code:

db("msg.db", "", "", ""):
  type
    User = object
      name: string
      email: string
      password: string
      avatar: string
      pushtoken: string
      registration: int64
      chats: string

var email = "[email protected]"
withDb:
  var u = User.getOne("email LIKE '?'", email)
  echo repr u

Implement migrations for adding missing columns and other schema modifications

When adding new fields to objects, reusing the same database throws the "column missing" error. For sqlite new columns can be added with the ALTER TABLE command. It would be great if this could either be done automatically, or explicitly via a function call, not sure what the best approach would be. This would prevent the need for migration scripts which can grow out of hand for cases where lots of fields get added over time. Since norm aims to make the usage of databases from Nim more convenient, I think this would be a welcome addition.

Auto foreign key doesn't work with exported types

That's because the fk boilerplate is added only to types that fully match by name with the type name of the fk field. Since the asterisk is a part of the type name, this match never happens for exported types.

template/generic instantiation of `insert` from here

I try build the example on nim 0.20.2:

import norm/sqlite
import logging
import unicode, sugar, options

db("petshop.db", "", "", ""):
  type
    User = object
      age: Positive
      name: string
      ssn: Option[int]

addHandler newConsoleLogger()

withDb:
  createTables(force=true)

withDb:
  var bob = User(
    age: 23,
    name: "bob",
    ssn: some 456
  )
  bob.insert()
  dump bob.id     
  debug(bob.id)

got build errors:

d:\sources\nim\main.nim(17, 1) template/generic instantiation of `withDb` from here
d:\sources\nim\main.nim(23, 6) template/generic instantiation of `insert` from here
C:\Users\user\.nimble\pkgs\norm-1.0.15\norm\sqlite.nim(90, 42) Error: attempting to call undeclared routine: 'join'

I try to commet line 90:
debug insertQuery, " <- ", params.join(", ")
in C:\Users\user.nimble\pkgs\norm-1.0.15\norm\sqlite.nim

It can be compiled successes.

Handle boolean type

To do that, we'll have to split rowutils for different backends. This requires refactoring, which is currently blocked until #19 is merged.

However, boolean type support for SQLite can be added now, because incidentally, rowutils for SQLit and Postgres are split already (had to do it to support NULL values with ndb #3).

  • SQLite
  • Postgres

Documentation: guide on one to many relationships

According to the tutorial:

Models can relate to each each with one-to-one, one-to-many, many-to-many relations. For example, a CartItem can have many Discounts, whereas as a single Discount can be applied to many Products.

However, I've so far been unable to figure out how to setup a one-to-many model relationship. I saw in another issue that seq/set types are unsupported (understandable), is there another way to define one-to-many? I've tried this approach:

type A = ref object of Model
  foo: string
type B = ref object of Model
  a1: A
  a2: A

Inserting B objects work as expected (two entries in table A, one entry in table B linking to both ids). But when selecting the B object, both a1 and a2 are populated with the same a1 data. Not sure if this is a bug, or if I'm making a mistake in my approach. Thanks for the help!

Automatic foreign key handling

Currently, Norm models are deliberately very close to the generated table schema. To define a one-to-many relation, you just mark a field with fk pragma and reference another model in it.

However, key benefit of having an ORM is that it gives you a nicer interface to your data, so you don't want a field that contains an id of the referenced object like in a DB, you want the referenced object itself. Currently, to achieve that, you should write a parser manually (see https://github.com/moigagoo/norm-sample-webapp/blob/develop/src/models.nim#L19)

To make Norm a more useful ORM, we should handle foreign keys automatically. This:

type
  User = object
    name: string
  Pet = object
    owner: User

should be equivalent to this:

type
  User = object
    name: string
 Pet = object
    owner {.
      fk: User,
      dbCol: "ownerId",
      dbType: "INTEGER NOT NULL",
      parseIt: User.getOne(it.i.int),
      formatIt: ?it.id
    .}: User

norm/postgres santax error

Error: unhandled exception: ERROR: syntax error at or near "user"
LINE 1: DROP TABLE IF EXISTS user CASCADE

import norm/postgres
import logging # Import logging to inspect the generated SQL statements.
import unicode, sugar, options


db("172.17.0.2", "postgres", "123", "postgres"):
    type
        User = object
            age: Positive
            name: string

    proc getUsers(): seq[User] =
        withDb:
            User.getMany(100, cond = "1")


addHandler newConsoleLogger()
withDb:
    # dbConn.exec(sql"""DROP TABLE IF EXISTS "user" CASCADE""")
    # ^ works but v doesn't
    createTables(force = true)

Also are you on discord I just have a few questions I'd like to ask

Table pragma has no effect on table name

I think this may be a regression, sorry but I don't have time to reproduce a minimal example:

  type
    User* {.table: "users".} = object
      nickname*: Option[string] ## Set via `/user/@id/set_nickname`
      email*: string

    SkinsOwnership* {.table: "skins_ownership".} = object
      skin* {.dbType: "INTEGER", parseIt: it.i.Skin, formatIt: ?it.int.}: Skin
      acquireTime* {.
        dbType: "INTEGER",
        parseIt: it.i.fromUnix().utc(),
        formatIt: ?it.toTime().toUnix()
      .}: DateTime
      userId* {.
        fk: User,
        onDelete: "CASCADE"
      .}: int

This ends up with:

DEBUG CREATE TABLE user (
        id INTEGER NOT NULL DEFAULT 0 PRIMARY KEY,
        nickname TEXT,
        email TEXT NOT NULL DEFAULT '',
)
DEBUG CREATE TABLE skinsownership (
        id INTEGER NOT NULL DEFAULT 0 PRIMARY KEY,
        skin INTEGER,
        acquireTime INTEGER,
        userId INTEGER NOT NULL DEFAULT 0 REFERENCES users (id) ON DELETE CASCADE
)

Note the REFERENCES pointing to users whereas the table name is user.

This used to work, but recently started failing for me once I inserted data into SkinsOwnership. Workaround is to remove the {.table: "users".}

UUID support?

I haven't seen anything covering use if UUID. Is this supported?

Norm 2.0

Norm needs a rewrite. Current implementation doesn't allow to implement certain things and unnecessarily complicates certain other ones. Also, the API doesn't separate configuration and business logic strong enough, which I vividly noticed while working on Norman.

Docs

The package is now mature enough to deserve proper documentation. The docs should consist of:

  • quickstart (installation, basic usage)
  • cookbook (feature overview, common patterns, edge cases)
  • API docs
  • contributor's guide

I can't yet decide on the tool to use. Deciding between:

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.