Code Monkey home page Code Monkey logo

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

kemal's Issues

Hello world example

Hi, I am trying run hello world example from tutorial, but I am getting such error:

➜  kemal_example git:(master) ✗ crystal build src/kemal_example.cr -d
Error in ./libs/kemal/kemal.cr:21: instantiating 'HTTP::Server::Context#params()'

    image = env.params["image"]
                ^~~~~~

in ./libs/kemal/kemal/context.cr:6: instantiating 'Kemal::ParamParser#parse()'

      @params ||= Kemal::ParamParser.new(@request).parse
                                                   ^~~~~

in ./libs/kemal/kemal/param_parser.cr:17: instantiating 'parse_request()'

    parse_request
    ^~~~~~~~~~~~~

in ./libs/kemal/kemal/param_parser.cr:21: instantiating 'parse_url_params()'

    parse_url_params
    ^~~~~~~~~~~~~~~~

in ./libs/kemal/kemal/param_parser.cr:38: undefined method 'url_params' for Nil

    if params = @request.url_params
                         ^~~~~~~~~~

================================================================================

Nil trace:

  ./libs/kemal/kemal/param_parser.cr:12

      def initialize(@request)
                     ^

  ./libs/kemal/kemal/param_parser.cr:12

      def initialize(@request)
                     ^~~~~~~

  ./libs/kemal/kemal/param_parser.cr:12

      def initialize(@request)

Crystal version: Crystal 0.10.2 (Wed Jan 13 20:13:28 UTC 2016), OSX El Capitan

kemal-chat crystal run error

has it got any new version ?
crystal run src/kemal_chat.cr
Error in ./libs/kemal/kemal.cr:5: instantiating 'OptionParser:Class#parse!()'
OptionParser.parse! do |opts|
^~~~~~
in /usr/local/Cellar/crystal-lang/0.11.1/src/option_parser.cr:54: instantiating 'parse(Array(String))'
parse(ARGV) { |parser| yield parser }
^~~~~
in /usr/local/Cellar/crystal-lang/0.11.1/src/option_parser.cr:54: instantiating 'parse(Array(String))'
parse(ARGV) { |parser| yield parser }
^~~~~
in ./libs/kemal/kemal.cr:5: instantiating 'OptionParser:Class#parse!()'
OptionParser.parse! do |opts|
^~~~~~
in ./libs/kemal/kemal.cr:7: instantiating 'Kemal:Module#config()'
Kemal.config.port = opt_port.to_i
^~~~~~
in ./libs/kemal/kemal/config.cr:5: instantiating 'Kemal::Config:Class#new()'
INSTANCE = Config.new
^~~
instantiating 'Kemal::Config#initialize()'
in ./libs/kemal/kemal/config.cr:13: instantiating 'read_file()'
read_file
^~~~~~~~
in ./libs/kemal/kemal/config.cr:37: undefined method 'working_directory' for Dir:Class
path = File.expand_path("config.yml", Dir.working_directory)

Middleware Order

Currently we don't have any middleware ordering. This can lead to wrong execution flows.

We need to order the handlers while bootstrapping the application. Something like config.bootstrap_handlers is required.

Include wrk command

Hello,

Could you include the wrk command you used in the README.md or an example/wrk.sh or something?

It is always great to see what people are running to load test a framework, and use that same command to load test other frameworks as well and/or validate the claims.

This framework looks great. Thanks for putting this together.

Hello World?

Thought I'd give kemal a quick test via the getting started information this evening, but ran into an apparent wall. When I get to the execution stage after a seemingly successful compilation, and execute the binary, I get no output whatsoever.

src/kemal_test.cr contains:

require "kemal"

get "/" do
  "Hello World!"
end

Any idea what may be going on here or where would I start to troubleshoot such a seemingly simplistic issue?

Filter middleware is order dependent

Hi again,

I was also looking at the Kemal::Middleware::Filter implementation and the before after will only work if Kemal::Middleware::Filter is the first middleware added. If another is added before this one it will silently fail.

I don't know if you wanted it specifically this way but if you want it to not be order dependent you could replace these lines in kemal/middleware/filters.cr:

def add_filters
  Kemal.config.add_handler Kemal::Middleware::Filter.new
end

def before(path = "*", options = {} of Symbol => String, &block : HTTP::Server::Context -> _)
  filter = Kemal.config.handlers.first as Kemal::Middleware::Filter
  filter.add :before, path, options, &block
end

def after(path = "*", options = {} of Symbol => String, &block : HTTP::Server::Context -> _)
  filter = Kemal.config.handlers.first as Kemal::Middleware::Filter
  filter.add :after, path, options, &block
end

With these (very little change but would make it order independent and would remove the need to call add_filters explicitly instead it will be called lazily if it hasn't been called before) =>

def add_filters
  unless filter = Kemal.config.handlers.any? { |handler| handler.is_a? Kemal::Middleware::Filter }
    filter = Kemal::Middleware::Filter.new
    Kemal.config.add_handler filter
  end
  filter
end

def before(path = "*", options = {} of Symbol => String, &block : HTTP::Server::Context -> _)
  filter = (Kemal.config.handlers.find { |handler| handler.is_a? Kemal::Middleware::Filter } || add_filters) as Kemal::Middleware::Filter
  filter.add :before, path, options, &block
end

def after(path = "*", options = {} of Symbol => String, &block : HTTP::Server::Context -> _)
  filter = (Kemal.config.handlers.find { |handler| handler.is_a? Kemal::Middleware::Filter } || add_filters) as Kemal::Middleware::Filter
  filter.add :after, path, options, &block
end

Again, if this is by design feel free to close this immediately, just thought I'd mention it. I doubt something as insignificant as this would be worthy of a pull request, but I could make one if necessary.

"undefined constant HTTP::WebSocketHandler::WebSocket"

ws("/") do |socket|
^~

in ./libs/kemal/kemal/dsl.cr:9: undefined constant HTTP::WebSocketHandler::WebSocket

def ws(path, &block : HTTP::WebSocketHandler::WebSocket -> _)
                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Using 0.8.0.

Sessions plugin

Hi, I started writing a session plugin because I want to use Kemal for my applications and I need to be able to work with sessions: https://github.com/Thyra/kemal-session
It's quite crude yet and needs a lot of improvements but I thought you might be interested...

A bit of missing installation information for beginners

Hi @sdogruyol,

Thanks for writing such an awesome yet simple framework. I' am learning Crystal and I stumble upon kemal while searching for some web development frameworks which I can use and contribute to. I just want to point-out a small thing in installation section. I didn't knew what shards is until I see a video presentation from Eurucamp 2015 but even then I struggled a bit while installing kemal. You mentioned in installation section to include a code snippet in shard.yml but putting that code was not running for me and I was getting following error:

Shards::ParseError: missing required attribute: name at 4:0

 1.     github: sdogruyol/kemal
 2.     branch: master`

I did a little research and found out that every shard.yml must contain a name attribute. It would be nice if we have this mentioned in installation section of kemal as well so that beginners don't find it difficult to install it :-).

If you think adding that is not necessary then it's fine as well, I just wanted to get in-touch regarding this thing and I' am really looking forward to contribute to kemal soon :-).

Thanks.

Custom logger

Hello,

I've been reading the documentation and browsing the code for a few days and it looks really cool ( 👍 ) so I was thinking of implementing a custom logger but I think a few things wouldn't work right now:

First here

config = Kemal.config
if config.logging
  logger = Kemal::LogHandler.new
  config.logger = logger

Could we maybe specify our own logger class in something like config.logger_class (I don't even know if Crystal would support that) and then it would get instantiated? (Of course our logger will have a write method)

And then here:

rescue ex
    Kemal::LogHandler::INSTANCE.write "Exception: #{ex.to_s}\n"
    return render_500(context, ex.to_s)

You call your logger explicitly instead of Kemal.config.logger.write (I'm sure it's on purpose, I just don't see it, I'm still not very comfortable with Crystal)

Do you think I could achieve this without too much trouble or is it just not possible ?

Kemal version tags don't support semver format

From shards' usage:

When libraries are installed from Git repositories, the repository is expected to have version tags following the semver format, prefixed with a v. Examples: v1.2.3 or v2.0.0-rc1.

So, currently I can't refer to a specific version of Kemal:

dependencies:
  kemal:
    github: sdogruyol/kemal
    version: 0.5.0
$ crystal deps 
Updating https://github.com/sdogruyol/kemal.git
git command failed: git ls-tree -r --full-tree --name-only v0.5.0 -- shard.yml ()

Could you please add a support of semver format ?

Should be error when TCP port is busy

Hello,

would nice to have an error when TCP 3000 is busy. Currently I had

$ crystal run src/chat.cr 
[development] Kemal is ready to lead at http://0.0.0.0:3000

msa@Mag2 ~/prj/vid/app/chat (master)
$ 

How can the context be Nil/False in `call(context)` from the `RouteHandler` ?

Hi,

I'm sorry to annoy you again but I couldn't resist, I spent a few hours on it but couldn't figure out how in this method:

def call(context)
  context.response.content_type = "text/html"
  response = process_request(context)
  response || call_next(context) # <========== this line
end

From file kemal/route_handler.cr line 16

The || call_next(context) would ever be reached ?

The response comes from process_request:

def process_request(context)
  url = context.request.path.not_nil!
  Kemal::Route.check_for_method_override!(context.request)
  lookup = @tree.find radix_path(context.request.override_method as String, context.request.path)
  if lookup.found?
    route = lookup.payload as Route
    context.request.url_params = lookup.params
    begin
      body = route.handler.call(context).to_s
      context.response.print body
      return context # <================== this return
    rescue ex
      return render_500(context, ex.to_s) <================== this return
    end
  end
  # Render 404 unless a route matches
  return render_404(context) <================== this return
end

The 3 returns above, all return the context object that was passed as the arg to the process_request method.

Also if the context was Nil the process_request wouldn't be reached so it's safe to assume the context passed to process_request is not Nil so when this same context is returned from process_request to call the || call_next part would never be reached either right?

Even if it was Nil or False what could there be after this since the RouteHandler is added by Kemal in at_exit as the last middleware just before launching the server?

I hope I'm not just wasting your time with useless questions but it's driving me crazy 😕

Kemal is not thread-safe because HTTP::Server#listen isn't threadsafe

I have been trying hard to remove env. And while doing that I found out that Kemal is not thread-safe .
Because HTTP::Server is not thread-safe by-default.
I have found a way to make it thread-safe by using most established technique i.e. creating a process for each incoming request.
This will not only make Kemal thread-safe which will be beneficial for us in future, but it will allow us to assign context as global variable and we can use that global variable everywhere like redirect, render.
We can also expose params as a method and this will completely eliminate need of having env.params.

I discovered this thread-safety issue while creating a global variable in requests. I was putting some sleep in requests as well to notice the behaviour of that global variable. And to my expectation, it did what I was anticipating. I stored content-type as text/plain for one of the requests in a global variable but due to sharing of global variable among all requests, it was being applied to all requests.
I tried the exact same thing with new forked-based server implementation and that issue vanished .
Please let me know what you think about this?

Bugs with the refactoring of http std lib in crystal 0.11.0.

There's a lot of bugs related to the new context object in the http server refactoring for crystal 0.11.0.

Error in ./libs/kemal/kemal.cr:35: instantiating 'HTTP::Server#listen()'

  server.listen
         ^~~~~~

in macro 'spawn' /opt/crystal/src/concurrent/concurrent.cr:37, line 8:

  1.   
  2.     ->(
  3.       
  4.         __arg0 : typeof(server.accept),
  5.       
  6.       ) {
  7.       spawn do
  8.         handle_client(
  9.           
 10.             __arg0,
 11.           
 12.         )
 13.       end
 14.     }.call(server.accept)
 15.   

        handle_client(
        ^~~~~~~~~~~~~

instantiating 'handle_client(TCPSocket+)'
in /opt/crystal/src/http/server/server.cr:157: instantiating 'HTTP::Handler+#call(HTTP::Server::Context)'

        @handler.call(context)
                 ^~~~

in ./libs/kemal/kemal/middleware/http_basic_auth.cr:20: undefined method 'headers' for HTTP::Server::Context

      if request.headers[AUTH]?
                 ^~~~~~~

================================================================================

HTTP::Server::Context trace:

  ./libs/kemal/kemal/middleware/http_basic_auth.cr:19

        def call(request)

Problem with static files

I have a problem with css. In the app directory I have public/styles.css, views/index.ecr and src/app.cr
app.cr looks like

require "kemal"

module App::Crystal
  get "/" do
    render "views/index.ecr"
  end
end

index.ecr has link-tag <link rel="stylesheet" href="/styles.css">
When open localhost:3000 html renders well but css request has 404. Trying different pathes in link like public/styles.css, /public/styles.css, styles.css, /styles.css doesn't work.

post method return 404

require "kemal"
get "/a" do
  "a"
end

post "/b" do
  "b"
end
curl http://localhost:3000/a
curl http://localhost:3000/b -X POST -d '{"bla":12}'
2016-01-25 22:45:33 +0300 | 200 | GET /a - 91µs
2016-01-25 22:47:08 +0300 | 404 | POST /b - 65µs

Exception Logging

Currently we are wrapping the exception and outputting it into the ecr view.

However sometimes the stacktrace is needed for further debugging that's why we need to also output the exception to STDOUT.

Examples with template rendering

New to ruby/crystal ecosystem, so don't know about ecr template. Googled it, found nothing. Can you please give provide source for the template and maybe a some examples?

thanks.

Config to prevent Kemal server running.

HTTP server of Kemal always runs even if exit() is called explicitly, because server.listen is called in at_exit.

However, sometimes I want Kemal to exit without server.listen.
For example:

  • Illegal command-line parameters are given.
  • Another process of same Kemal app is already running.

Do you think how about adding Kemal.config.run property?
It should be true by default to cause no change to default behavior.
When it is false, Kemal silently exits without service.

if already_running?
  Kemal.config.logger.write "[#{Kemal.config.env}] another process is running.\n" if Kemal.config.logger
  Kemal.config.run = flase
  exit(1)
end

It is possible by adding a few lines to "kemal.cr" and "kemal/config.cr" .
I have tested it in my forked branch.

Request Body Parameters

Currently we only support parsing query parameters and nothing else.

We need to parse the body.

Issue with Ambience

App file looks like

require "kemal"
require "ambience"

module HotWater::Crystal
  ENV["env"] ||= "development"
  amb = Ambience::Application.new("~/hot_water-crystal/config/environment.yml", ENV["env"])
  amb.load

  get "/" do
    # render "views/index.ecr"
    p ENV
  end
end

But localhost:3000/ returns | 404 | GET / - (50µs)

Router Performance

Hi,

I benchmarked kemal on my laptop and I'm getting only 5000 req/s for a hello world application. Building in release mode results in about 7200 req/s.

For a comparision, using moonshine which is another sinatra clone I get 13000 in dev mode and 19000 in release mode.

require "kemal"
require "json"

get "/api/v1/ping" do
  {:ping => "pong"}.to_json
end
require "moonshine"
require "json"

include Moonshine
include Moonshine::Utils::Shortcuts

app = App.new

app.get "/api/v1/ping", do |request|
  ok({:ping => "pong"}.to_json)
end

port = ENV.has_key?("PORT") ? ENV["PORT"] : "5000"
app.run(port.to_i)
$ crystal --version
Crystal 0.10.1 (Fri Jan  8 21:13:35 UTC 2016)
$ uname -s
Darwin

$ crystal build kemal.cr -o bin/kemal
$ bin/kemal -p 5000
$ wrk -c 800 -t 100 -d20s http://127.0.0.1:5000/api/v1/ping
Running 20s test @ http://127.0.0.1:5000/api/v1/ping
  100 threads and 800 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     8.09ms   55.57ms   1.61s    98.71%
    Req/Sec   544.39    484.16     3.21k    68.63%
  101412 requests in 20.10s, 9.96MB read
  Socket errors: connect 0, read 517, write 0, timeout 76
Requests/sec:   5044.91
Transfer/sec:    507.45KB

$ crystal build --release kemal.cr -o bin/kemal
$ bin/kemal -p 5000
$ wrk -c 800 -t 100 -d20s http://127.0.0.1:5000/api/v1/ping
Running 20s test @ http://127.0.0.1:5000/api/v1/ping
  100 threads and 800 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     8.20ms   68.49ms   1.84s    98.82%
    Req/Sec     0.94k   789.94     6.55k    71.84%
  146596 requests in 20.10s, 14.40MB read
  Socket errors: connect 0, read 17, write 0, timeout 126
Requests/sec:   7294.91
Transfer/sec:    733.77KB

$ crystal build moonshine.cr -o bin/moonshine
$ ./bin/moonshine
$ wrk -c 800 -t 100 -d20s http://127.0.0.1:5000/api/v1/ping
Running 20s test @ http://127.0.0.1:5000/api/v1/ping
  100 threads and 800 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    57.72ms   10.70ms 108.12ms   75.91%
    Req/Sec   132.25     41.11     0.91k    73.79%
  262255 requests in 20.10s, 21.01MB read
  Socket errors: connect 0, read 623, write 0, timeout 0
Requests/sec:  13044.96
Transfer/sec:      1.05MB

$ cr build --release moonshine.cr -o bin/moonshine
$ ./bin/moonshine
$ wrk -c 800 -t 100 -d20s http://127.0.0.1:5000/api/v1/ping
Running 20s test @ http://127.0.0.1:5000/api/v1/ping
  100 threads and 800 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    39.22ms    6.82ms 103.44ms   78.84%
    Req/Sec   198.26     36.60   480.00     69.29%
  395779 requests in 20.10s, 31.71MB read
  Socket errors: connect 0, read 423, write 0, timeout 0
Requests/sec:  19690.01
Transfer/sec:      1.58MB

As a comparision, running sinatra with puma (1 worker, 16 threads):

$ PORT=5000 WEB_CONCURRENCY=1 puma -C config/puma.rb
$ wrk -c 800 -t 100 -d20s http://127.0.0.1:5000/api/v1/ping
Running 20s test @ http://127.0.0.1:5000/api/v1/ping
  100 threads and 800 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     7.47ms    3.76ms  60.84ms   76.13%
    Req/Sec     1.07k   111.97     1.25k    77.86%
  43017 requests in 20.09s, 7.71MB read
  Socket errors: connect 0, read 501, write 0, timeout 0
Requests/sec:   2140.76
Transfer/sec:    393.07KB

Handling http request type

Hello;

I want to seperate my logic according to http request type(GET, POST, PUT etc.). But i couldn't make it.
for example,

get "/" do |env|
  "GET REQUEST"
end

post "/" do |env|
  "POST REQUEST"
end

response is always "GET REQUEST"
also when i try to run only

post "/" do |env|
  "POST REQUEST"
end

i can get same response with GET or PUT request.

Problem running tests

How can I run tests when using kemal which starts a server while running tests and does not exit?

kemalcr.com down :/

I tried to read the doc at kemalcr.com but it seems to be unresponsive :/

Kemal suppresses runtime-errors

If you execute this short piece of code:

require "kemal"

get "/" do
  "Hello, World!"
end

if true
  raise "This is an Error!"
end

get "/hi" do
  "Hello, World!"
end

the results are quite weird: There is nowhere a notice of the error (neither logs nor in the browser) and you can open / but if you open /hi in the browser window, kemal says Kemal doesn't know this way.

I understand this is because / gets registered, then the program crashes and kemal's at_exit is executed, so everything after the error simply gets dropped.

Wouldn't it be better if kemal would clearly report the error and abort? I think it would make debugging much easier and a half-working webserver which doesn't know half of its supposed pages is anyway not helpful ;-).

render_XXX

Hi there! What am I doing wrong?
In view.cr I see methods render_404 and other.
If I use code

  get "/" do
    render_404 #or return render_404
  end

and then route it, kemal returnes me 200 and response #<HTTP::Response:xxxxxxxx>.
Am I missing something?

Instead of passing `Kemal::context.new` as `env` in route's callback, we should have something like Express.js

Crystal is quite a fun programming language to work with. I have been trying to remove env.redirect etc. to simplify method calls to something like redirect. I actually want to do this because in my opinion when kemal has support for templates having template getting rendered as env.render is not going to look cool.

Due to some core differences in Ruby and Crystal I' am unable to achieve this so far because there is no self in Crystal inside blocks which makes sense but this also means that we cannot apart env from method calls inside defined routes' callback.

However, there is a simple solution. Instead of passing something like env, we should pass something like req and res. For example,

  get "/" do |req, res|
    # important stuff here
  end

This will make method invocations more expressive and we can call appropriate methods on proper objects. For example to access params we can call req.params and to redirect we can call res.redirect and to render we can do res.render.

This is what Express.js is doing and I think it's quite elegant.

Handling multipart/form-data

This is actually needed for file uploads and multipart forms.

Currently we don't have any multipart form parsers for Crystal. Thinking of writing this as a seperate shard and then making a plugin for Kemal.

kemal-jwt middleware

Hello:

How can i approach the implementation of jwt in Kemal? When to enode the jwt token and how to verify the jwt signature and respond with 401 if is invalid?

The code in the below repository is a fork from one of the jwt ruby libraries.
https://github.com/Ravenstine/jwt.cr

Measured performance is not so fast

I have measured performance by ab command.
Here is the code.

require "kemal"

get "/" do
  "test"
end
$ crystal build --release server.cr
$ ./server -e production &
$ ab -n 10000 -c 100 http://localhost:3000
...
Requests per second:    1144.30 [#/sec] (mean)
Time per request:       87.390 [ms] (mean)
Time per request:       0.874 [ms] (mean, across all concurrent requests)
Transfer rate:          74.87 [Kbytes/sec] received
...

Using macbook pro (CPU: 3.1 GHz Intel Core i7, MEMORY: 16 GB 1867 MHz DDR3)

The result is about 100 times slower than expected.
Are there missing configuration options?

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.