Giter VIP home page Giter VIP logo

mill's Introduction

☴ Mill

Packagist Build

Mill is an annotation-based DSL for documenting a REST API and assisting you in creating OpenAPI or API Blueprint specifications for your API.

For detailed documentation, check out the wiki.

Features

  • Compile versioned API documentation into OpenAPI or API Blueprint specifications.
  • Automatically generate API changelogs
  • Production ready

Installation

composer require erunion/mill

mill'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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mill's Issues

Support referencing a representation by its label

With the new MSON work in #42, the syntax for referencing another representation in a representation data is:

@api-data director (\Mill\Examples\Showtimes\Representations\Person) - Director

This is fine, but it's a bit... wordy. It would be nice if instead of that you could just do:

@api-data director (Person) - Director

Where Person is being pulled from the @api-label annotation atop the \Mill\Examples\Showtimes\Representations\Person class.

The major problem I see with handling this is that this would require that we parse out representations first, so we have that label data in memory for lookups.

Markdown links in param descriptions collide with enums

@api-param {string} content_rating +MOVIE_RATINGS+ 
    [MPAA rating](http://www.mpaa.org/film-ratings/)

Having a parameter description with a Markdown link causes the link to get parsed without the link content.

[
    'capability' => 'MOVIE_RATINGS'
    'deprecated' => false
    'description' => '(http://www.mpaa.org/film-ratings/)'
    'field' => 'content_rating'
    'required' => true
    'type' => 'string'
    'values' => Array &1 (
        0 => 'MPAA rating'
    )
    'version' => false
    'visible' => true
]

Remove dependency on PHP method naming

Currently mill requires your PHP method to be named after an HTTP method. This seems like an unnecessary restriction. Lets discuss possible solutions in comments below.

Parameter tokens with overridden descriptions end up weird.

<parameterTokens>
    <token name="direction">{string} direction (optional) The direction that the results are sorted.</token>
</parameterTokens>
@api-param:private {direction} [asc|default|desc] Sort direction

Is compiled into API Blueprint as:

- `direction` (enum[string]) - The direction that the results are sorted. Sort direction
    + Members
        + `asc`
        + `default`
        + `desc`

The problem with fixing this is that right now we're doing a straight string replace for the token, and that doesn't care about the existing data.

I don't exactly know how to fix this other than running a quick parse of the existing parameter (pre-token replacement) to parse out its data, then drop the token in, re-parse and replace the tokens' data with the previously parsed content.

This is also going to be problematic with the coming MOSN work (#42), but that's going to most likely require revisiting the entire concept of parameter tokens.

Content-Type annotation lacks versioning support

@api-contentType currently lacks support for versioning, which might be useful if you once had an endpoint returning application/json, but now returns a custom mime-type like application/vnd.vimeo.channel.

"init" command for initializing a new mill.xml

Creating a mill.xml file is a bit... complicated. And tedious.

Build an init command that will walk you through, wizard-style, everything to set it up.

  • Add controller directories, classes, excludes
  • Add representation directories, classes, excludes
  • Add error representations
  • Add capabilities
  • Add scopes
  • Add parameter tokens
  • Add URI segment translations

"statistics" command for getting useful data about your documentation

  • Commonly used:
    • Parameter names
    • Representation dataset identifiers
    • Words
    • Content types
    • HTTP codes
  • Less common (may help detecting typos):
    • Parameter names
    • Representation dataset identifiers
    • Words
    • Content types
    • HTTP codes
  • What representation datapoints that are missing sample data.
  • What resource action parameters that are missing sample data.

"lint" command

We should have a lint command, that does everything that generate does when compiling your documentation. Right now, validation is halted at the first error, so instead of that, this command should build up a list of all the errors in your documentation and spit out a failure or success at the end.

Having this, it'll allow people to integrate this into their build processes alongside things like PHPUnit, PHPCS, or static analysis checkers.

@api-version doesn't get attached to loaded @api-see elements

When using @api-version alongside a @api-see element, the specified version is not being applied to the loaded annotations from @api-see, but instead all versions.

Example:

/**
 * @api-label Play representation
 * @api-field play
 * @api-type representation
 * @api-version >=3.3
 * @api-see \PlayResponse::_json play
 */

We're loading the play representation behind >=3.3, but what happens when you compile this is that the >=3.3 Blueprint files get the "Play representation" label, and content, while other versioned Blueprint files get everything, except for "Play representation".

What should be happening is that only >=3.3 gets the play representation content. All other versions should have no knowledge of this data.

Support for documenting enum values

Right now we have support for neither documenting @api-param or @api-options enumerated values.

@api-param:public {sort} [relevant|date|alphabetical|plays|likes|comments|duration]
@api-options [by|by-sa|by-nd|by-nc|by-nc-sa|by-nc-nd]

I'm imagining something like this:

@api-param:public {sort} [relevant|date|alphabetical|plays|likes|comments|duration]
@api-paramEnumValue {sort} comments This sorts by the amount of comments on the object.
@api-options [by|by-sa|by-nd|by-nc|by-nc-sa|by-nc-nd]
@api-optionsEnumValue {by-sa} Attribution-ShareAlike 2.0 Generic 

This could also allow us to start documenting the default value in an enum:

@api-paramEnumValue {sort} date *default* Date that a video was uploaded.

Replace the custom parameter and representation DSL with MSON

https://github.com/apiaryio/mson

Basically, we're running into issues adding new things (like examples) into the Mill DSL for @api-param and representation annotations (see #41). Instead of trying to reinvent the wheel ourselves, we should just move over to MSON and be done with it.

Advantages of MSON over our existing description DSL:

  1. It's an open specification.
  2. It's easy to use.
  3. We didn't build it.

Disadvantages:

  1. We'll need to build an MSON parser in PHP.

Solving this would be three parts:

  • Build a Mill MSON-flavor syntax parser.
  • Replace @api-param syntax with MSON
  • Replace @api-field, @api-type, @api-label, and @api-options in representations with a single new @api-data annotation, that also uses MSON for describing the piece of data.

MSON flavor

Since we require capabilities, and writing enum/options/members on the same line as the parameter, we'll need to have our own MSON flavor:

@api-param:visibility field `example` (type, required, capability) - Description 
    + Members
        - `option1` - "option1" description
        - `option2` - "option2" description 
@api-data field `example` (type, required, capability) - Description
    + Members
        - `option1` - "option1" description
        - `option2` - "option2" description 

This should allow us to retain the core parts of MSON portability, while also retaining some flexibility for Mill-specific features like capabilities, and single line definitions.

Unfortunately, this means we'll need to build our own parser, but at least the spec part of this is easy to understand, and will be easy to implement.

@api-param

Swapping out the @api-param DSL for MSON would be relatively easy (pending an MSON parser) as all of that logic is contained within ParamAnnotation::parse. Instead of manually parsing the docblock with regex, we'd feed it into the MSON parser and get back our data, and then send that along to the overall Mill parsing engine.

For example:

@api-param:public {page}
@api-param:public {per_page}
@api-param:public {string} query Search query.
@api-param:public {sort} [relevant|date|alphabetical]
@api-param:public {direction} [asc|desc]

Will become:

@api-param:public page
@api-param:public per_page
@api-param:public query (string) - Search query.
@api-param:public sort [relevant|date|alphabetical]
@api-param:public direction [asc|desc]

@api-data

Doing the same will also be simple, since everything is contained within the FieldAnnotation:parser method.

@api-label Director
@api-field director
@api-type string
@api-version >=3.3

Will become:

@api-data director `JJ Abrams` (string) - Director
@api-version >=3.3

This'll allow us to cut back on the annotation complexity of representation data by killing @api-label, @api-field, @api-type, and @api-options.

@api-see should support static methods

@api-see right now only lets you reference classes by their full FQN, @api-see \Full\Class::method.

We should change this so you can do things like @api-see static::method so you can self-reference methods within the response you're using. This is helpful if you have documentation in a base class, but want to include docs that are available within an abstract method.

Programmatically, I think we can do a detection on the see annotation and if it has static:: or self::, swap that out with the FQN of the class representation that that exists within.

Should be fairly easy.

Trim whitespace from parameter values during parsing

Parameters with values that are broken up on multiple lines end up having unnecessary/unwanted whitespace surrounding them when generated into API Blueprint files.

* @api-param:public {filter} [CC
*                  |CC0
*                  |CC-BY
*                  |CC-BY-SA
*                  |CC-BY-ND
*                  |CC-BY-NC
*                  |CC-BY-NC-SA
*                  |CC-BY-NC-ND
*                  |in-progress
*                  |upload_date
*                  |duration
*                  |minimum_likes
*                  |categories
*                  |trending]
*          The CC filters will show only those videos with the applicable creative commons licenses.
*          See our <a href="https://vimeo.com/creativecommons">Creative Commons</a> page for more.
- `filter` (enum[string]) - Filter to apply to the results.  The CC filters will show only those videos with the applicable creative commons licenses. See our <a href="https://vimeo.com/creativecommons">Creative Commons</a> page for more.
    + Members
        + `CC `
        + `CC-BY `
        + `CC-BY-NC `
        + `CC-BY-NC-ND `
        + `CC-BY-NC-SA `
        + `CC-BY-ND `
        + `CC-BY-SA `
        + `CC0 `
        + `categories `
        + `duration `
        + `in-progress `
        + `minimum_likes `
        + `trending`
        + `upload_date `

Better deprecation support

This is partly related to #21.

Right now, the Mill deprecated annotation decorator makes it awful to document why something was deprecated. There is also no way to designate that a URI was deprecated a new version as URI's cannot be versioned.

You also cannot deprecate representation datasets, which is kind of silly.

Get Travis working for PHP 5.4

The last version of PHPUnit to support PHP 5.4 was 4.8.*, but I'm having trouble getting that installed locally:

$ composer require --dev phpunit/phpunit 4.8.34
You are running composer with xdebug enabled. This has a major impact on runtime performance. See https://getcomposer.org/xdebug
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - phpunit/phpunit 4.8.34 conflicts with phpunit/phpunit-mock-objects[3.4.3].
    - phpunit/phpunit 4.8.34 conflicts with phpunit/phpunit-mock-objects[3.4.3].
    - phpunit/phpunit 4.8.34 conflicts with phpunit/phpunit-mock-objects[3.4.3].
    - Installation request for phpunit/phpunit 4.8.34 -> satisfiable by phpunit/phpunit[4.8.34].
    - Installation request for phpunit/phpunit-mock-objects (locked at 3.4.3) -> satisfiable by phpunit/phpunit-mock-objects[3.4.3].


Installation failed, reverting ./composer.json to its original content.

Until we drop support for 5.4 (#13), we should at least get tests running against it.

Verify generator supports SemVer patch versions.

Right now, SemVer "patch" versions, like "1.1.1" are not compiled.

We should treat them just like every other version, so they'd get their own directory in the output, and the generator should have them split apart as well.

Merge `exclude` and `ignores` in the config file

<exclude> and <ignores> in the config file are basically doing the same thing: ignoring classes from being parsed for documentation. Let's merge them and go with just <exclude>.

Also replace the isRepresentationIgnored method in the config class to pull from this merged array.

If a minVersion higher than any other present version annotations, throw an error.

If an @api-minVersion is set on a resource, but there are @api-version annotations on that same resource that are less than the minimum version, we should throw an error during generation.

/**
 * @api-label LABEL
 *
 * @api-uri:private {Group} /uri
 *
 * @api-contentType application/json
 * @api-minVersion 2.0
 *
 * @api-return:public {collection} \Representation
 *
 * @api-version 1.0 
 * @api-return:public {object} \Representation
 */

Mill needs a logo

Mill needs a logo. Thinking a pixel art windmill.

I loved the windmills in Fez.

sk940go

Something like that, but simpler, would be pretty awesome.

Surface resource and resource action descriptions in API Blueprint files

Not exactly sure how we're going to handle this because those descriptions can contain Markdown, and it'll be very difficult to merge that with the Markdown in the Blueprint files. Nonetheless we should figure something out, even if it's just converting Markdown headers into bold elements or something.

Allow representations on `@api-return` to be optional

When you're doing a DELETE, you usually aren't returning any data from the request, so it doesn't make sense to document it with a representation.

/**
 * Delete a movie.
 *
 * @api-label Delete a movie.
 *
 * @api-uri:private {Movies} /movies/+id
 * @api-uriSegment {/movies/+id} {integer} id Movie ID
 *
 * @api-contentType application/json
 * @api-scope delete
 *
 * @api-return:private {deleted}
 *
 * @api-throws:private {404} \Vimeo\Mill\Examples\Showtimes\Representations\Error If the movie could not be found.
 */

Endpoint parameters are unalphabetized.

...
* @api-param:public {string} name (optional) The new title for the video
* @api-param:public {string} license [by|by-sa|by-nd|by-nc|by-nc-sa|by-nc-nd|cc0] (optional) Set the Creative Commons license
* @api-param:public {string} description (optional) The new description for the video
...

When generated into Blueprint, or even just from the generator array, these parameters are in the order that they were typed.

To keep things clean, and friendly to the person actually consuming documentation, Mill should alphabetize (recursively if necessary on dot-notation field) parameters.

Simplify error reporting

Everywhere we're currently reporting errors back as __CLASS__::__METHOD__. This is fine, except that __METHOD__ already includes the class that it's part of, so our error messages end up being __CLASS::__CLASS__::__METHOD__.

  • Stop passing a class around to error messages, and just return error messages with the __METHOD__.

@api-warning support on representations

There are some instances in the Vimeo API where we have data within a representation that depends upon a service call, and that call may fail from time to time. The problem right now is that when that call fails, we don't then want the entire endpoint to fail.

Instead, we've come up with a solution to return an array of "warnings" with information on what data in the representation we were unable to fill out for the developer.

The way to document this, I believe, is with a new @api-warning annotation. It'll look the same as an @api-throws, just without an HTTP status code.

@api-warning \Warning\Representation (\Warning\Error:CODE) Some warning message.

This annotation will only be available on representations:

/**
 * @api-data metadata.connections.watched_videos (object) - Information about the 
 *      videos this user has watched.
 * @api-see \App\Representation\Connection::create metadata.connections.watched_videos
 * @api-warning \App\Representation\Warning 
 *      (\App\Warnings\PlayProgress::WATCHED_VIDEOS_UNAVAILABLE) Watched videos 
 *      are currently unavailable.
 */

For configuration, I think we can go with the same setup that we have for error representations since this is a little similar:

<representations>
    <filter>
        ...
    </filter>

    <errors>
        ...
    </errors>
    
    <warnings>
        <class name="\App\Representation\Warning" method="create" />
    </warnings>
</representations>

  • Build an @api-warning annotation to be used within representations.
    • This is an optional annotation.
    • There should not be a limit on the amount of warnings that a dataset can have.
    • As we have with @api-throws annotations, there should be validation in place to verify that the attached warning representation is valid, and the (required) error code is present.
      • As is the case with @api-throws, error codes should be able to be strings or a FQN static callable. If it is not callable, we should throw a validation error.
  • Replicate the same system that we have in place for error representation configs for warnings.
  • In the generated API Blueprint file, surface any representation warnings up to the + Response and display them in a bulleted list similar to how we're currently surfacing multiple @api-throws and error codes.

Class-level uri annotations

Depending on code organization, uri annotations can become redundant when applied to every resource action. For example, a class that only defines the GET and POST actions for the /todos resource would end up using the same uri annotations for both methods. In those cases it could be cleaner to throw the annotation in the class's docblock.

"changelog" command for generating documentation changelogs

It would be amazing to have a changelog command that, when run, would spit out a changelog (in Markdown, probably) for the life of your API.

Starting at the configured sinceApiVersion, it'll run through every version it knows about and do diffs between the previous/next version.

Unclear right now on the verbiage that the changelog would have, but it'd be cool to get something functional first.

Add a latestApiVersion declaration into mill.xml

  • Add a latestApiVersion declaration into mill.xml so people can tell Mill what their latest API version is, and also use that to pull their own latest API docs.

Down the line, we can then use this to do validation on their API versioning to prevent issues where might accidentally typo a version and then blowout their docs.

Support for description dictionaries

Right now, @api-throws has support for a dictionary-like type and subtype system where you can do {User} as your throws description to generate a description of "If the User cannot be found.".

It might be worth investigating at looking to replace that with a configurable dictionary of these types of common return, error, and parameter descriptions that you could easily drop in.

For example:

@api-throws:private {403} \ErrorRepresentation If the user was not found.

Imagine setting up a dictionary in your config like:

<dictionaries>
    <description id="user.not_found">If the user was not found.</description>
</dictionaries>

And then doing:

@api-throws:private {403} \ErrorRepresentation {user.not_found}

We should also retain the current sprintf-like functionality and support something like:

<dictionaries>
    <description id="common.not_found">If the %s was not found.</description>
</dictionaries>
@api-throws:private {403} \ErrorRepresentation {common.not_found:"user"}

Just an idea to consolidate and have consistent documentation error messaging.

Support deprecated tag when generating output

The goal is to be able to indicate that a field, endpoint, parameter etc. is "the wrong way", and has been replaced with a better way.

eg from the Vimeo API: The upload_link parameter is now HTTPS, which means there is no need for the upload_link_secure parameter because the urls are now identical. We should have the option to exclude upload_link_secure from most docs, and include it in others with a clear warning that upload_link_secure should no longer be used, and will be removed in future versions.

Ideally we would be able to explain what the better way is alongside the deprecation annotation.

Documenting array parameter contents

Right now, there is no way to document the contents of a non-enum array parameter.

How can we build something where you can document something like:

@api-param:public {array[floats]} time_codes Array of all timecodes in this video with associated notes.

Support for a new "alias" @api-uri decorator

There are instances where you have a resource action that is serving multiple URI endpoints:

* @api-uri:private {Albums\Videos} /albums/#album_id/videos/#clip_id
* @api-uriSegment {/albums/#album_id/videos/#clip_id} {integer} album_id Album ID
* @api-uriSegment {/albums/#album_id/videos/#clip_id} {integer} clip_id Video ID
*
* @api-uri:public {Albums\Me\Albums\Videos} /me/albums/#album_id/videos/!clip_id
* @api-uriSegment {/me/albums/#album_id/videos/!clip_id} {integer} album_id Album ID
* @api-uriSegment {/me/albums/#album_id/videos/!clip_id} {integer} clip_id Video ID
*
* @api-uri:public {Albums\Users\Albums\Videos} /users/+user_id/albums/#album_id/videos/!clip_id
* @api-uriSegment {/users/+user_id/albums/#album_id/videos/!clip_id} {integer} user_id User ID
* @api-uriSegment {/users/+user_id/albums/#album_id/videos/!clip_id} {integer} album_id Album ID
* @api-uriSegment {/users/+user_id/albums/#album_id/videos/!clip_id} {integer} clip_id Video ID

However, you may, in your custom rendering of documentation to smash all three of those into a single entry. I think we can solve this with a new alias decorator system for @api-uri annotations. Designate a URI as an alias, and it'll be absorbed into the other URI for your own use.

* @api-uri:private:alias {Albums\Videos} /albums/#album_id/videos/#clip_id
* @api-uriSegment {/albums/#album_id/videos/#clip_id} {integer} album_id Album ID
* @api-uriSegment {/albums/#album_id/videos/#clip_id} {integer} clip_id Video ID
*
* @api-uri:public:alias {Me\Albums\Videos} /me/albums/#album_id/videos/!clip_id
* @api-uriSegment {/me/albums/#album_id/videos/!clip_id} {integer} album_id Album ID
* @api-uriSegment {/me/albums/#album_id/videos/!clip_id} {integer} clip_id Video ID
*
* @api-uri:public {Users\Albums\Videos} /users/+user_id/albums/#album_id/videos/!clip_id
* @api-uriSegment {/users/+user_id/albums/#album_id/videos/!clip_id} {integer} user_id User ID
* @api-uriSegment {/users/+user_id/albums/#album_id/videos/!clip_id} {integer} album_id Album ID
* @api-uriSegment {/users/+user_id/albums/#album_id/videos/!clip_id} {integer} clip_id Video ID
  • Add a new :alias decorator that's only supported on URI annotations.
  • During parsing, copy the aliased annotations over to the non-alias/primary annotation.
  • You cannot have an equal amount of aliases and URI's. If any aliases are present, there must be only one non-alias/primary.
  • API Blueprint generation will remain the same. All three different URI's will be compiled separately.

@api-return return types should be constantized

Currently there's a list of return code types in ReturnAnnotation::findReturnCodeForType that should be converted into class constants on that annotation class.

  • collection
  • directory
  • object
  • ok
  • string
  • created
  • accepted
  • added
  • deleted
  • exists
  • updated
  • notmodified

Versioning doesn't allow for differing documentation

/**
 * @api-data pictures (\App\PictureRepresentation) - The active picture for this video.
 */

/**
 * @api-data pictures (array<object>) - The video's thumbnail images
 * @api-version <3.2
 */

Problem stems from us storing representation data in an identifier-keyed array when parsing it out in the RepresentationParser.

Documenting parameter and representation examples

For the API Blueprint files (and coming OAI work planned for in 2.0, #16), to be useful in a mock server environment like Drakov, we need some sort of way to document @api-param and representation data examples.

I think documenting them on representations would be easily accomplished with an @api-example annotation:

/**
 * @api-label Director
 * @api-field director
 * @api-type string
 * @api-example JJ Abrams
 */

Thankfully, this isn't really necessary for enum's as Drakov picks the first item in the enum as the example. For parameters, however, it's a bit more complicated because of the existing language syntax in place for documenting them.

The current idea that I've been running with is to document examples within backticks.

@api-param:public {string} director (optional) `JJ Abrams` Name of the director.

And compiled into the Blueprint, it would appear as:

- `director`: `JJ Abrams` (string) - Director

Drop support for PHP 5.4

I'd like to heavily use ::class in unit tests to enable better IDE support when building the library, and can't do this and also support PHP 5.4

Deprecate parameter tokens for a new trait system.

With the new MSON work being done in mill#42, the current parameter token system is no longer viable.

The way it was originally constructed was that it would str_replace content from the token, "{string} direction (optional) The direction that the results are sorted." onto the @api-param annotation: "@api-param:private {direction} [asc|default|desc] Sort direction". This has resulted in funky API Blueprint content being generated:

- `direction` (enum[string]) - The direction that the results are sorted. Sort direction
    + Members
        + `asc`
        + `default`
        + `desc`

With he move to MSON for parameters and representations, this format will no longer be viable because of the wide difference between MSON content and how a string replace would work.

So the solution to fix this is to construct a new "trait" system.

The basic idea is that we'd replace the parameterTokens config with traits. You would load traits with a @api-trait annotation, and then specify additional data to override that trait with, like a description, or new enum values.

  • Replace the <parameterTokens> config XML declaration with a <traits> config.
  • Traits will be configured as MSON within <trait> blocks:
<trait name="filter_playable">
    `true` (string, default, optional) - Choose between only videos that are playable, and only videos that are not playable.
        + Members
            - `true`
            - `false`
</trait>
  • You will load traits within a resources as: @api-trait filter_playable
  • Traits should support overloading data by just adding that data onto the @api-trait annotation:
@api-trait filter_playable - This is an overloaded description
    + Default: `yes`
    + Members
        - `yes`
        - `no`
  • Traits should be able to be versioned.
  • For response data that is only present when a trait on that resource is present, you should be able to specify this in your representation data declaration with a @api-traitEnabled trait_name annotation.
    • The use-case for this might be something like response pagination where one resource is paginated, but another isn't, despite them returning the same representation.
  • In compiled API Blueprint files, traits should look as regular parameters and representation data (because essentially they are).
  • Traits should support visibilities.

For some additional background, see:

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.