Git Product home page Git Product logo

pager-api's Introduction

Code Climate Test Coverage Issue Count

Pager API

Pager

API Pagination done right. Pager API is a library to help you add meta information and the adequate header with pagination information based on the JSON API standard for your Rails app.

Table of contents

Quick Start

pager_api depends on Pagy, Kaminari, WillPaginate to handle pagination. You need to add one of these gems to your Gemfile before the pager_api gem:

# gem 'will_paginate'
# gem 'kaminari'
# gem 'pagy'
gem 'pager_api'

And then execute:

% bundle

Configuration

This step is totally optional

The gem comes with an installer for you to configure it, for example to switch between pagination handlers or whether or not to include the Link header or meta information. To install it you just need to run:

% rails g pager_api:install

This will create a file under the initializers directory called pager_api.rb. You can easily configure it there to meet your needs.

By default pager_api uses Pagy. Configure the pager_api.rb initializer in order to use WillPaginate or Kaminari.

We highly recommend you use Active Model Serializers for rendering your JSON responses

A note on Active Model Serializers 0.10

Currently the pager-api gem needs some configuration to work nice with Active Model Serializers, just add a file under config/initializers on your rails project:

% touch config/initializers/active_model_serializers.rb

And a this line:

ActiveModelSerializers.config.adapter = :json

Or even

ActiveModelSerializers.config.adapter = :json_api

Usage

In the controller where you are providing a paginated collection, you may have something like this:

class UsersController < ApplicationController
  def index
    users = User.page(params[:page]).per(15)

    render json: users,
           meta: {
             pagination: {
               per_page: 15,
               total_pages: 10,
               total_objects: 150
             }
           }
  end
end

With pager_api it is really easy to achieve the above by:

class UsersController < ApplicationController
  def index
    # You can have any scope for the User class in this case
    # You can even send the paginated collection
    paginate User.unscoped, per_page: 15
  end
end

This will output a json object like:

{
    "users": [
    	...
    ],
    "meta": {
        "pagination": {
            "per_page": 15,
            "total_pages": 1,
            "total_objects": 15,
            "links": {
                "first": "/api/users?page=1",
                "last": "/api/users?page=1"
            }
        }
    }
}

As you can see, the pagination metadata also includes the links information for the first and last page, but it will also create the next and the prev keys when necessary.

By default it will also include a Link header with the following information:

# Link: <http://example.com/api/v1/users?page="2">; rel="next",
# <http://example.com/api/v1/users?page="5">; rel="last",
# <http://example.com/api/v1/users?page="1">; rel="first",
# <http://example.com/api/v1/users?page="1">; rel="prev",

The header will be created with the corresponding first, last, prev and next links.

Bug tracker & feature request

Have a bug or a feature request? Please open a new issue. Before opening any issue, please search for existing issues.

Contributing

Please submit all pull requests against a separate branch. Although pager_api does not have tests yet, be a nice guy and add some for your feature. We'll be working hard to add them too.

In case you are wondering what to attack, we have a milestone with the version to work, some fixes and refactors. Feel free to start one.

Thanks!

Heroes

Abraham Kuri

License

Code and documentation copyright 2015 Icalia Labs. Code released under the MIT license.

pager-api's People

Contributors

anadaniel avatar brahimdahmani avatar brunoocasali avatar dunkstewart avatar fivell avatar grandman avatar kurenn avatar mayra-cabrera avatar morellan avatar vivekmarakana avatar

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

Watchers

 avatar  avatar  avatar  avatar  avatar

pager-api's Issues

pagination.per_page shows 0 when per_page is not defined in neither params nor as a options

I'm using Kaminari to do pagination with default_per_page = 10. Now when I write paginate user.orders without specifying per_page, the navigation object is like this:

"meta": {
    "pagination": {
      "per_page": 0,
      "total_pages": 3,
      "total_objects": 23,
      "links": {
        "first": "/v1/user/orders?page=1",
        "last": "/v1/user/orders?page=2",
        "next": "/v1/user/orders?page=2"
      }
    }
  }

Metadata not appearing

Hi, I'm using this gem to communicate with an iOS app, my config is as so

PagerApi.setup do |config|

  # Pagination Handler
  # User this option to meet your pagination handler, whether is :kaminari or :will_paginate
  config.pagination_handler = :kaminari

  # Includes Pagination information on Meta
  #
  config.include_pagination_on_meta = true

  # Includes Pagination information on a Link Header
  #
  config.include_pagination_headers = true

  # Set the Total-Count Header name
  config.total_count_header = "X-Total-Count"
end

and I 'm using it like so in my controllers

def index paginate Registration.all, per_page: 1 end

The response I get is as below

[ { "paid_at" : null, "status" : "pending", "sponsorship_id" : null, "user_id" : 1, "course_id" : 1, "updated_at" : "2016-06-01T10:39:34.262+08:00", "transaction_id" : null, "fees_paid_cents" : 0, "referrer_id" : null, "graduate_status" : "not_graduated", "payment_method" : null, "total_paid_cents" : 0, "tax_paid_cents" : 0, "id" : "c0d47566-f5b3-4217-81df-79ef3bb8267f", "book_entry_record_url" : "https:\/\/sg.qbo.intuit.com\/app\/salesreceipt?txnId=", "referrer_paid_at" : null, "referrer_transaction_id" : null, "created_at" : "2016-06-01T10:39:34.262+08:00", "total_paid_currency" : "MYR", "tax_paid_currency" : "MYR", "fees_paid_currency" : "MYR" } ] [ { "paid_at" : null, "status" : "pending", "sponsorship_id" : null, "user_id" : 1, "course_id" : 1, "updated_at" : "2016-06-01T10:39:34.262+08:00", "transaction_id" : null, "fees_paid_cents" : 0, "referrer_id" : null, "graduate_status" : "not_graduated", "payment_method" : null, "total_paid_cents" : 0, "tax_paid_cents" : 0, "id" : "c0d47566-f5b3-4217-81df-79ef3bb8267f", "book_entry_record_url" : "https:\/\/sg.qbo.intuit.com\/app\/salesreceipt?txnId=", "referrer_paid_at" : null, "referrer_transaction_id" : null, "created_at" : "2016-06-01T10:39:34.262+08:00", "total_paid_currency" : "MYR", "tax_paid_currency" : "MYR", "fees_paid_currency" : "MYR" }, { "paid_at" : null, "status" : "pending", "sponsorship_id" : null, "user_id" : 2, "course_id" : 1, "updated_at" : "2016-06-01T10:39:34.337+08:00", "transaction_id" : null, "fees_paid_cents" : 0, "referrer_id" : null, "graduate_status" : "not_graduated", "payment_method" : null, "total_paid_cents" : 0, "tax_paid_cents" : 0, "id" : "2e0050ec-2e37-4a85-88b4-86c63c9a829d", "book_entry_record_url" : "https:\/\/sg.qbo.intuit.com\/app\/salesreceipt?txnId=", "referrer_paid_at" : null, "referrer_transaction_id" : null, "created_at" : "2016-06-01T10:39:34.337+08:00", "total_paid_currency" : "MYR", "tax_paid_currency" : "MYR", "fees_paid_currency" : "MYR" }, { "paid_at" : null, "status" : "pending", "sponsorship_id" : null, "user_id" : 3, "course_id" : 1, "updated_at" : "2016-06-01T10:39:34.361+08:00", "transaction_id" : null, "fees_paid_cents" : 0, "referrer_id" : null, "graduate_status" : "not_graduated", "payment_method" : null, "total_paid_cents" : 0, "tax_paid_cents" : 0, "id" : "a9c49040-efb5-42f5-ba44-a92020cd8c22", "book_entry_record_url" : "https:\/\/sg.qbo.intuit.com\/app\/salesreceipt?txnId=", "referrer_paid_at" : null, "referrer_transaction_id" : null, "created_at" : "2016-06-01T10:39:34.361+08:00", "total_paid_currency" : "MYR", "tax_paid_currency" : "MYR", "fees_paid_currency" : "MYR" }, { "paid_at" : null, "status" : "pending", "sponsorship_id" : 1, "user_id" : 4, "course_id" : 1, "updated_at" : "2016-06-01T10:39:34.386+08:00", "transaction_id" : null, "fees_paid_cents" : 0, "referrer_id" : null, "graduate_status" : "not_graduated", "payment_method" : null, "total_paid_cents" : 0, "tax_paid_cents" : 0, "id" : "5d7cd71f-4522-45de-974c-a7748918f377", "book_entry_record_url" : "https:\/\/sg.qbo.intuit.com\/app\/salesreceipt?txnId=", "referrer_paid_at" : null, "referrer_transaction_id" : null, "created_at" : "2016-06-01T10:39:34.386+08:00", "total_paid_currency" : "MYR", "tax_paid_currency" : "MYR", "fees_paid_currency" : "MYR" }, { "paid_at" : null, "status" : "pending", "sponsorship_id" : null, "user_id" : 5, "course_id" : 1, "updated_at" : "2016-06-01T10:39:34.411+08:00", "transaction_id" : null, "fees_paid_cents" : 0, "referrer_id" : null, "graduate_status" : "not_graduated", "payment_method" : null, "total_paid_cents" : 0, "tax_paid_cents" : 0, "id" : "3eb2f267-38ed-462a-aa2a-875a3e95e0db", "book_entry_record_url" : "https:\/\/sg.qbo.intuit.com\/app\/salesreceipt?txnId=", "referrer_paid_at" : null, "referrer_transaction_id" : null, "created_at" : "2016-06-01T10:39:34.411+08:00", "total_paid_currency" : "MYR", "tax_paid_currency" : "MYR", "fees_paid_currency" : "MYR" }, { "paid_at" : null, "status" : "pending", "sponsorship_id" : null, "user_id" : 6, "course_id" : 1, "updated_at" : "2016-06-01T10:39:34.433+08:00", "transaction_id" : null, "fees_paid_cents" : 0, "referrer_id" : null, "graduate_status" : "not_graduated", "payment_method" : null, "total_paid_cents" : 0, "tax_paid_cents" : 0, "id" : "9d96a727-f274-4586-973e-be477130ef26", "book_entry_record_url" : "https:\/\/sg.qbo.intuit.com\/app\/salesreceipt?txnId=", "referrer_paid_at" : null, "referrer_transaction_id" : null, "created_at" : "2016-06-01T10:39:34.433+08:00", "total_paid_currency" : "MYR", "tax_paid_currency" : "MYR", "fees_paid_currency" : "MYR" }, { "paid_at" : null, "status" : "pending", "sponsorship_id" : null, "user_id" : 8, "course_id" : 1, "updated_at" : "2016-06-01T10:39:34.470+08:00", "transaction_id" : null, "fees_paid_cents" : 0, "referrer_id" : null, "graduate_status" : "not_graduated", "payment_method" : null, "total_paid_cents" : 0, "tax_paid_cents" : 0, "id" : "0ee9c953-64a0-4647-96e9-274cfa7f6a50", "book_entry_record_url" : "https:\/\/sg.qbo.intuit.com\/app\/salesreceipt?txnId=", "referrer_paid_at" : null, "referrer_transaction_id" : null, "created_at" : "2016-06-01T10:39:34.470+08:00", "total_paid_currency" : "MYR", "tax_paid_currency" : "MYR", "fees_paid_currency" : "MYR" }, { "paid_at" : null, "status" : "pending", "sponsorship_id" : null, "user_id" : 9, "course_id" : 1, "updated_at" : "2016-06-01T10:39:34.493+08:00", "transaction_id" : null, "fees_paid_cents" : 0, "referrer_id" : null, "graduate_status" : "not_graduated", "payment_method" : null, "total_paid_cents" : 0, "tax_paid_cents" : 0, "id" : "5ce71f97-84dc-42bf-872a-ce6f483de19e", "book_entry_record_url" : "https:\/\/sg.qbo.intuit.com\/app\/salesreceipt?txnId=", "referrer_paid_at" : null, "referrer_transaction_id" : null, "created_at" : "2016-06-01T10:39:34.493+08:00", "total_paid_currency" : "MYR", "tax_paid_currency" : "MYR", "fees_paid_currency" : "MYR" }, { "paid_at" : null, "status" : "pending", "sponsorship_id" : null, "user_id" : 10, "course_id" : 1, "updated_at" : "2016-06-01T10:39:34.518+08:00", "transaction_id" : null, "fees_paid_cents" : 0, "referrer_id" : null, "graduate_status" : "not_graduated", "payment_method" : null, "total_paid_cents" : 0, "tax_paid_cents" : 0, "id" : "f2de4b51-24a4-4628-b144-bf386bc27ac5", "book_entry_record_url" : "https:\/\/sg.qbo.intuit.com\/app\/salesreceipt?txnId=", "referrer_paid_at" : null, "referrer_transaction_id" : null, "created_at" : "2016-06-01T10:39:34.518+08:00", "total_paid_currency" : "MYR", "tax_paid_currency" : "MYR", "fees_paid_currency" : "MYR" }, { "paid_at" : null, "status" : "pending", "sponsorship_id" : null, "user_id" : 11, "course_id" : 1, "updated_at" : "2016-06-01T10:39:34.542+08:00", "transaction_id" : null, "fees_paid_cents" : 0, "referrer_id" : null, "graduate_status" : "not_graduated", "payment_method" : null, "total_paid_cents" : 0, "tax_paid_cents" : 0, "id" : "9917b6c7-57cd-4ef9-968c-d9feaf0b24d4", "book_entry_record_url" : "https:\/\/sg.qbo.intuit.com\/app\/salesreceipt?txnId=", "referrer_paid_at" : null, "referrer_transaction_id" : null, "created_at" : "2016-06-01T10:39:34.542+08:00", "total_paid_currency" : "MYR", "tax_paid_currency" : "MYR", "fees_paid_currency" : "MYR" } ]

I cant get the metadata to appear.
I noticed changing the paginate method in kaminari.rb from render options to return options makes it work.

[Solved] undefined method 'paginate'

I had the problem that a few others have had with the undefined paginate method. I'm using Rails 5.1.2 and have a regular Rails app, rather than one that's just an API. I set everything up per the documentation using Kaminari and couldn't get it to work. Tried another gem and it mentioned that for Rails 5+ you needed to add an include line. I then looked in this gem and added the following to my applicaton_controller.rb:

include PagerApi::Pagination::Kaminari

Now everything works as expected. I saw in the code where this should be getting included automatically but that's apparently not happening, so this fixes it. I thought I would post this in case anyone else runs into this issue.

Pager-api expecting kaminari despite config

Hi there! I'm trying to get this gem working, but am having difficulty getting it to work with will_paginate.

I'm using rails 4.2.7.1, will_paginate-3.1.0. My PagerApi Initializer looks like this:

PagerApi.setup do |config|
  config.pagination_handler = :will_paginate
end

My Problem:
When I try to call paginate(....) from a controller I get undefined method 'per'....

It appears that, despite my config, pager-api is trying to use kaminari. If I pry right before my pagination call and look at show-method paginate, I see...
Owner: PagerApi::Pagination::Kaminari

However PagerApi.pagination_handler returns :will_paginate

After some local testing it appears that the initializer is not being loaded before line 2 of hooks.rb is executed, so the gem defaults to using the kamari handler. This bombs because I don't have kaminari installed.

Possible Solution:
Looking at the rails documentation, I think the existing railtie is being called before the setup block is called. If I'm missing something obvious, please let me know.

I was able to get this working by changing railtie.rb to look like:

Module PagerApi
  class Railtie < Rails::Railtie
    config.after_initialize do
      require 'pager_api/hooks'
    end
  end
end

Support for Pagy

Pagy is a relatively new "pagination gem that outperforms the others in each and every benchmark and comparison".

Would this paginator be something Pager API could support?

Rails 6.0.0.rc1 Support

Hi, im getting this warning when start Rails server with "rails s":

DEPRECATION WARNING: Initialization autoloaded the constants ActionText::ContentHelper and ActionText::TagHelper.

I'm using Rails 6.0.0.rc1, pager_api 0.2.4 and kaminari 1.1.1

Thanks!

How can I send data in hash with other keys?

My code looks like this, I need to render other keys
unless params[:search].present?
@response.merge!({code: 0, message:"failed, please provide search term"})
else
data = Model.search(params[:search])
@response[:data] = { search_result: data, size: data.count }
@response.merge!({code: 1, message:"success"})
end

`render status: :ok, json: @response`

But as pager api it internally renders the given records.

Include to Interactions

I use gem 'active_interaction' and pager-api not included to class Interactions

code example

class Interactions::Users::List < ActiveInteraction::Base
  include Contracts::Core
  include Contracts::Builtin

  array :role, default: nil do
    :string
  end

  Contract None => ActiveRecord::Relation
  # @return [ActiveRecord::Relation<User>]
  def execute
    users = User.all
    users = users.where(role: role) if given?(:role)
    users
  end
end

It would be good else PagerApi be included

Remove "data" and "links" from JSON response

Pager-api is really useful, thank you for making it! I have one tiny issue that I can't figure out. I only want the pagination data in the headers, which works fine, but I'd like to purge a couple of things from my JSON response. When I do an index action, for instance, my response looks something like this:

{
    "data": [
        {
            "id": ...
        }
    ],
    "links": {}
}

I would like to remove the first and last, and just have it look like my responses looked before adding pager-api, i.e.

{
    "id": "29jf9s",
    "name": "etc. etc."
    ...
}

I'm using active-model-serializers 0.10, if that's relevant as well.

EDIT

My index action looks like this:

def index
    paginate SubOrder.unscoped
end

And my serializer looks like this:

class SubOrderSerializer < ActiveModel::Serializer
    attributes :id, :num_pages, :cost, :url, :date_created, :date_fulfilled
end

undefined method `paginate'

after install and configure PagerApi & Kaminari, i got #<NoMethodError: undefined method 'paginate' for #<V1::ProjectsController:0x005563f62bff70>>

env :

  • rails 5.0.0.1
  • kaminari 0.17.0
  • PagerApi 0.1.1

Conflict with serializer

I have kind of a two-fold issue. I'm running the bug-fixes branch that has been mentioned in two other threads here, and it's working, but not the way I'd like it to. In the initializer, I set include_pagination_on_meta to false, because I only want the link headers. However, when using Postman to run my index action, I indeed get the meta information in the body. This also invalidates the serializer I had set up, and shows every attribute of the given records. I assume if I can make the meta data disappear, the serializer will return to normal, so I would like to fix that first. Any idea what is causing this?

issue with nested associations

render json: @posts, include: ['author', 'comments', 'comments.author']

How could I use the above statement with the below line:

paginate @posts, per_page: 15

I am not able to add the nested associated objects. please suggest.

Add support for specifying custom `:meta` options

I'm currently using this with a project and it's working well for including pagination metadata in a JSONAPI using AMS and Kaminari. However I want to extend the returned metadata to include additional values other than the existing pagination keys; currently that's not possible as the entire meta: options passed to render method are overwritten.

I've opened a PR that suggests an approach - essentially this just merges rather than overwrites any existing meta: options. Let me know what you think ๐Ÿ˜

#37

0.2.3 release has broken pagination header behaviour

Hello ๐Ÿ‘‹

It looks as though version 0.2.3 released on Rubygems returns the total number of pages as X-Total-Count. Code in master looks OK, so I presume the 0.2.3 release has a since-fixed bug. I can't verify, as there are no tags for releases.

Suggested resolution would be:

  1. Fix typo on this line of the config file
  2. Tag and release a new version to Rubygems

Add custom :page param

On a conversation with @ddnexus and @cseelus we were discussing about the possibility of having a a configuration option for the :page param, rather than hard coding it to :page.

This should work for the 3 supported pagination gems(Pagy, Kaminari, WillPaginate)

Here are some notes:

From a (resolved) Pagy issue:

The ability to add arbitrary params similar to will_paginates :params option or Kaminaris option with the same name would be helpful for a number of use cases:

[โ€ฆ] (not applicable for API pagination)
Passing search/form params around, when Pagy is used in combination with a search function
Passing in a controller/action name (see will_paginate or Kaminaris docs)

undefined method `each' for 1:Fixnum

def index
  paginate Loan.unscoped
end
Started GET "/loans" for 127.0.0.1 at 2017-04-05 11:31:31 -0400
Processing by LoansController#index as */*
  Parameters: {"loan"=>{}}
  CACHE (0.0ms)  SELECT COUNT(*) FROM "loans"
  CACHE (0.0ms)  SELECT  "loans".* FROM "loans" LIMIT $1 OFFSET $2  [["LIMIT", 25], ["OFFSET", 0]]
Completed 200 OK in 129ms (Views: 127.6ms | ActiveRecord: 0.0ms)


Unexpected error while processing request: undefined method `each' for 1:Fixnum
  /thin-1.7.0/lib/thin/response.rb:74:in `block in headers='
  /rack-2.0.1/lib/rack/utils.rb:454:in `block in each'
  /rack-2.0.1/lib/rack/utils.rb:453:in `each'
  /rack-2.0.1/lib/rack/utils.rb:453:in `each'
  /thin-1.7.0/lib/thin/response.rb:69:in `headers='
  /thin-1.7.0/lib/thin/connection.rb:102:in `post_process'
  /thin-1.7.0/lib/thin/connection.rb:53:in `process'
  /thin-1.7.0/lib/thin/connection.rb:39:in `receive_data'
  /eventmachine-1.2.2/lib/eventmachine.rb:194:in `run_machine'
  /eventmachine-1.2.2/lib/eventmachine.rb:194:in `run'
  /thin-1.7.0/lib/thin/backends/base.rb:73:in `start'
  /thin-1.7.0/lib/thin/server.rb:162:in `start'
  /rack-2.0.1/lib/rack/handler/thin.rb:22:in `run'
  /rack-2.0.1/lib/rack/server.rb:296:in `start'
  /railties-5.0.0.1/lib/rails/commands/server.rb:79:in `start'
  /railties-5.0.0.1/lib/rails/commands/commands_tasks.rb:90:in `block in server'
  /railties-5.0.0.1/lib/rails/commands/commands_tasks.rb:85:in `tap'
  /railties-5.0.0.1/lib/rails/commands/commands_tasks.rb:85:in `server'
  /railties-5.0.0.1/lib/rails/commands/commands_tasks.rb:49:in `run_command!'
  /railties-5.0.0.1/lib/rails/commands.rb:18:in `<top (required)>'
  /bin/rails:9:in `require'
  /bin/rails:9:in `<top (required)>'
  /spring-2.0.0/lib/spring/client/rails.rb:28:in `load'
  /spring-2.0.0/lib/spring/client/rails.rb:28:in `call'
  /spring-2.0.0/lib/spring/client/command.rb:7:in `call'
  /spring-2.0.0/lib/spring/client.rb:30:in `run'
  /spring-2.0.0/bin/spring:49:in `<top (required)>'
  /spring-2.0.0/lib/spring/binstub.rb:31:in `load'
  /spring-2.0.0/lib/spring/binstub.rb:31:in `<top (required)>'
  /bin/spring:14:in `require'
  /bin/spring:14:in `<top (required)>'
  bin/rails:3:in `load'
  bin/rails:3:in `<main>'

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.