Coder Social home page Coder Social logo

splitio / ruby-client Goto Github PK

View Code? Open in Web Editor NEW
25.0 22.0 15.0 13.03 MB

Ruby SDK client for Split Software

Home Page: https://split.io

License: Other

Ruby 96.82% C 1.85% Java 1.33%
split splitsoftware feature-flags feature-toggles feature-toggle control-rollout ruby sdk ab-testing

ruby-client's Introduction

Split SDK for Ruby

build status Documentation

Overview

This SDK is designed to work with Split, the platform for controlled rollouts, which serves features to your users via feature flags to manage your complete customer experience.

Twitter Follow

Compatibility

The Ruby SDK support Ruby version 2.5.0 or later and JRuby or 9.1.17 o later.

Also the Ruby SDK has been tested as a standalone app as well as using the following web servers:

  • Puma
  • Passenger
  • Unicorn

For other setups, please reach out to [email protected].

Getting started

Below is a simple example that describes the instantiation and most basic usage of our SDK:

require 'splitclient-rb'

split_factory = SplitIoClient::SplitFactory.new('YOUR_SDK_KEY')
split_client = split_factory.client

begin
  split_client.block_until_ready
rescue SplitIoClient::SDKBlockerTimeoutExpiredException
  puts 'SDK is not ready. Decide whether to continue or abort execution'
end

treatment = split_client.get_treatment('CUSTOMER_ID', 'FEATURE_FLAG_NAME');

if treatment == 'on'
  # insert code here to show on treatment
elsif treatment == 'off'
  # insert code here to show off treatment
else
  # insert your control treatment code here
end

For multi-process environments you also need to setup Split Synchronizer. See Sharing state: Redis integration

Please refer to our official docs to learn about all the functionality provided by our SDK and the configuration options available for tailoring it to your current application setup.

Submitting issues

The Split team monitors all issues submitted to this issue tracker. We encourage you to use this issue tracker to submit any bug reports, feedback, and feature enhancements. We'll do our best to respond in a timely manner.

Contributing

Please see Contributors Guide to find all you need to submit a Pull Request (PR).

License

Licensed under the Apache License, Version 2.0. See: Apache License.

About Split

Split is the leading Feature Delivery Platform for engineering teams that want to confidently deploy features as fast as they can develop them. Split’s fine-grained management, real-time monitoring, and data-driven experimentation ensure that new features will improve the customer experience without breaking or degrading performance. Companies like Twilio, Salesforce, GoDaddy and WePay trust Split to power their feature delivery.

To learn more about Split, contact [email protected], or get started with feature flags for free at https://www.split.io/signup.

Split has built and maintains SDKs for:

For a comprehensive list of open source projects visit our Github page.

Learn more about Split:

Visit split.io/product for an overview of Split, or visit our documentation at help.split.io for more detailed information.

ruby-client's People

Contributors

chillaq avatar ezesplit avatar facundocabrera avatar fvitale avatar github-actions[bot] avatar goldensun8891 avatar hjewkes avatar htp avatar ignaciosplit avatar israphel avatar lucas-aragno avatar maksimf avatar mmelograno avatar mredolatti avatar mvpgomes avatar nicozelaya avatar patricioe avatar punnie avatar sanzmauro avatar senhorcastor avatar splitadricejas 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

Watchers

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

ruby-client's Issues

Occasionally split_manager returns empty list of splits

Not sure where this should go, but we're running a ruby app that uses the split ruby sdk(7.0.0). The split-synchronizer(2.6.1) is running, attached to a redis instance. When the split-synchronizer first starts, everything is working fine, but after some time(maybe about a day or so), the split_manager will stop returning splits - ie, split_names is empty. If a split is changed in split.io though, that split starts coming back in split_names(and splits).

The split-synchronizer dashboard is fine, and looks like this:
image

The 400s in the error log seem to show up regardless of whether the issue is happening or not.

The health check is also fine:
image

At this point, the only thing that works is if split-synchronizer is restarted, then everything goes back to normal. Does anyone have any idea what might be the issue?

`fix_latencies` is running every restart by each app server, affecting Redis performance

Hello!

We have a Split setup using the Ruby client in consumer mode, with Redis to communicate with the split synchronizer. The fix_latencies fix found here has the effect of consuming a ton of Redis resources. This is run on each restart of the servers, and on each app server where we use the split gem. With a large number of app servers, and a shared Redis instance, the repeated scan commands are causing Redis performance issues.

Could we have an option to disable the fix? It has already been run, and doesn't need to be re-run each restart.

Thanks!

Faraday 2.0+ compatibility

Can you tell if or when you will support faraday version 2.0 and above? This limitation is preventing us from evaluating Split's services.

JSON Gem Dep

Trying to move forward on bundling this gem and hitting issues. I noticed there is a twiddle wakka dep for the JSON gem to ~> 1.8. Checking the released for the gem does show recent updates for that version.

https://rubygems.org/gems/json/versions

I can explore setting ~> 1.8 in my Rails v5 app's Gemfile, but is there a reason we are locked down to 1.8? Can the dep be open ended or >= 1.8 or something?

block_until_ready doesn't timeout if the api key is invalid

Hi,

I would like to handle the case where the api key for split is invalid and we recover by assuming any feature's using split are disabled by default.

I was expecting to just rescue after creating the split factory, client, or calling block_until_ready.
However, it seems the block_until_ready method doesn't timeout when the api key is invalid.

Would you have an alternative suggestion such as a way to validate the API key first or should block_until_ready timeout eventually?

Current behavior:

00:28:00 web.1       | Unexpected status code while fetching splits: 404. Check your API key and base URI
00:28:00 web.1       | [splitclient-rb] Unexpected exception in fetch_splits: #<RuntimeError: Split SDK failed to connect to backend to fetch split definitions> Split SDK failed to connect to backend to fetch split definitions
00:28:02 worker.1    | Unexpected status code while fetching splits: 404. Check your API key and base URI
00:28:02 worker.1    | [splitclient-rb] Unexpected exception in fetch_splits: #<RuntimeError: Split SDK failed to connect to backend to fetch split definitions> Split SDK failed to connect to backend to fetch split definitions

That repeats indefinitely.

Thanks!

7.2.3 performance issues

We've noticed after upgrading from 7.2.2 to 7.2.3 we're experiencing performance issues. Our test suite grinds to a halt after a few seconds. Memory leak maybe?

I've forked and can take a closer look at the diff between the versions but wanted to open an issue before I did.

Redis.exists? deprecation warning from redis v4.2.2

We recently upgraded some components in our application and are running 7.0.3 of your gem.

We're using a redis-namespace gem which has upgraded our redis gem to version 4.2.2. I see you are also including the redis gem in your gem-spec: https://github.com/splitio/ruby-client/blob/master/splitclient-rb.gemspec#L58

We're now getting a deprecation warning on this line of code in your gem: https://github.com/splitio/ruby-client/blob/master/lib/splitclient-rb/cache/adapters/redis_adapter.rb#L38

I noticed in your test files that you're already appending the ? so it won't be an issue there, but you'll need to make adjustment to use the ? in your code before your gem can upgrade to redis 4.3.

The change is obviously very quick and you already have test coverage for the exists method. We'd love to resolve the deprecation warning to clean up our log file output. Would you mind making the change or I'm happy to fork and submit a PR if you'd prefer I submit the change for consideration.

Let me know.

Gem Namespace & Directory Practice

Congrats, this gem looks really nice and I'm really looking forward to using this it with the Split.io service.

While doing a technical review, I noticed that the gem has several namespace issues. These appear to be simple fixes but wanted your thoughts before doing a pull request.

General Directory & Namespace

Below are some reference articles on best practices on how to structure the gem lib directory. Having been bitten by these issues before, I consider these really important because file load conflicts are very hard to fix from the outside.

The directories lib/cache, lib/engine, and lib/exceptions would need to be in lib/splitcient-rb/... to avoid conflicts. Good news is that each file has the proper module namespace, so a simple git mv for each of these directories followed by changes to paths in sliptclient-rb.rb should be sufficient.

File Namespace

The other issue I see is that lib/splitclient-rb_utilities.rb defines a top level Utilities constant. This file name is OK since it is prefixed with the gem name, however, that module name is very ambiguous. Suggest changing the contents to use the SplitIoClient::Utilities namespace and making changes in the code to remove the :: in places and everything should work fine.

What do you think? Is this something that you can fix or would you like a PR for it?
Again, thanks for the great work in the gem.

Error during a parsing event

I'm using your service and the 7.3.4 gem version. I'm running into this error when updates come back:

Error during parsing a event: #<JSON::ParserError: unexpected token at 'data:
{"id":"xbWhZupEwC:0:0","timestamp":1711579820508,"clientId":"pri:MzU2Nzc3Mzc4NQ==","channel":"MjYyNDc3NDY0OQ==_NDAwMTc1NDgxNw==_splits","data":"{\"type\":\"SPLIT_UPDATE\",\"changeNumber\":1711579819987,\"pcn\":1711579723688,\"c\":2,\"d\":\"eJy8kk2L2zwcxL+KmbO8WLbjF93Cs8vT0DSBTdpSgimyLSdiZTnIMiUN/u5FSTabpqXQS2/S6P8y/o2PsIY3jazWh71Y8FaAgetOH9pu6EEgazA0eZTWSRr4ZRnkPqVC+FlEGz9LYtrENEuSPAaBPrc/LhfLZz+Lcr/uNLfC7/dKWpDXTVOluopb2WkwGgS/0VdC1GA0m+R5mKRJTNCfFJ/GYZYmNM4Iesvt0INh+t969ukJBC9SKVfVcNULglo0fFB2bQS3rdAWDJ0GQbXjeisWQ1sKA0ZTSidpntE8z1K3x/Zgm4KAq20HFhJUnW7kdjAnZz3YcTxptbzcN8e3q4MIhuflfL78uAZBy221E+Z/0w17MFfZllK7xZguHt8KznNexGEllKhsZ1zxTTJ3qXBrjSwH6/Rm0LXhshcG43Xepefzu9n6aT5bOStabLlruOD5tpNWKNnbD+eOR265W3rVwTagYQSCMKDRZAICSpM8Px2iKI7D10OEYhyLkWDPjb3BYu/R9/K7OGU+krvXprk+B2NBoHgp1E8f50ntOVvehoYR8c6eiPfw8FDAjfvHGfyCejqff33/9GV1T/ovwPwRi6N2A+byd3tmUAJjMf4IAAD//4H5OTc=\"}","encoding":"json"}'>

This is when running in local dev. I'm not sure if these issues are happening elsewhere.

Upgrading to the latest gem isn't much of an option for us. We're trying to use Split to help us migrate off of this old project.

Thanks!

Cache Adapter Questions

This is just a lazy support question and I would be fine sending an email or discussing elsewhere. How much can one use the default MemoryAdapters::MapAdapter with a typical Rails application that takes a "descent" amount of web traffic?

  • What is stored in memory? Splits & Envs only? Per user treatments?
  • Will the memory grow unbound or does it get collected?

splits_repository.splits is significantly more costly than splits_repository.split_names

After adding a feature which requires inspecting the traffic_type for each split, we noticed a significant spike in our response times.

We tracked it down to this change:

irb(main):024:0> Benchmark.bm {|x| 10.times { x.report { Adapters::SplitAdapter.new.send(:split_manager).splits } }}
       user     system      total        real
   0.080000   0.028000   0.108000 (  0.404583)
   0.088000   0.028000   0.116000 (  0.400634)
   0.080000   0.048000   0.128000 (  0.417616)
   0.068000   0.044000   0.112000 (  0.406767)
   0.068000   0.040000   0.108000 (  0.397871)
   0.072000   0.068000   0.140000 (  0.412072)
   0.080000   0.036000   0.116000 (  0.392635)
   0.080000   0.048000   0.128000 (  0.390702)
   0.092000   0.036000   0.128000 (  0.414035)
   0.084000   0.048000   0.132000 (  0.459113)
irb(main):025:0> Benchmark.bm {|x| 10.times { x.report { Adapters::SplitAdapter.new.send(:split_manager).split_names } }}
       user     system      total        real
   0.008000   0.000000   0.008000 (  0.013941)
   0.004000   0.000000   0.004000 (  0.011022)
   0.008000   0.000000   0.008000 (  0.013846)
   0.008000   0.000000   0.008000 (  0.011736)
   0.000000   0.004000   0.004000 (  0.010728)
   0.004000   0.000000   0.004000 (  0.011632)
   0.008000   0.000000   0.008000 (  0.011978)
   0.004000   0.000000   0.004000 (  0.010795)
   0.012000   0.000000   0.012000 (  0.014070)
   0.004000   0.000000   0.004000 (  0.012502)

I noticed this line seems to be rather costly. Is there a less costly way to determine traffic_types?

Silence the logger when running rails console or when running tests

By default, the SDK logs out to STDOUT. This can be very distracting and noisy when a developer starts a rails console session or when running tests. The SDK should detect these run modes and configure the default logger to be silent, or redirect to the Rails logger instead of STDOUT.

Here's a snippet of what I do in my split configuration.

options = {}
options[:logger] = NullObject.new if defined?(Rails::Console) || Rails.env.test?
...
SplitIoClient::SplitFactory.new(api_key, options)

Note: It's unfortunately that I have to explicitly pass in a NullObject which is not standard in Ruby. Perhaps the options parser should distinguish between passing in an explicit nil vs not configuring the value.

SDK not sync configuration changes in Rails console

I tried to integrate split.io Ruby SDK with Rails, but found out the in-memory SDK in Rails console process never sync data with split.io website.

in config/initializers/split_client.rb:

split_factory =
  SplitIoClient::SplitFactory.new(ENV['SPLIT_IO_API_KEY'])
SPLIT_CLIENT = split_factory.client
Rails.configuration.split_factory = split_factory

begin
  SPLIT_CLIENT.block_until_ready
rescue SplitIoClient::SDKBlockerTimeoutExpiredException => error 
  Rails.logger.error '=> Split.io SDK is not ready'
  raise error
end

in config/puma.rb:

# Puma can serve each request in a thread from an internal thread pool.
# The `threads` method setting takes two numbers: a minimum and maximum.
# Any libraries that use thread pools should be configured to match
# the maximum value specified for Puma. Default is set to 5 threads for minimum
# and maximum; this matches the default thread size of Active Record.
#
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
threads threads_count, threads_count

# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
#
port        ENV.fetch("PORT") { 3030 }

# Specifies the `environment` that Puma will run in.
#
environment ENV.fetch("RAILS_ENV") { "development" }

# Specifies the number of `workers` to boot in clustered mode.
# Workers are forked webserver processes. If using threads and workers together
# the concurrency of the application would be max `threads` * `workers`.
# Workers do not work on JRuby or Windows (both of which do not support
# processes).
#
workers ENV.fetch('WEB_CONCURRENCY') { 2 }

# Use the `preload_app!` method when specifying a `workers` number.
# This directive tells Puma to first boot the application and load code
# before forking the application. This takes advantage of Copy On Write
# process behavior so workers use less memory.
#
preload_app!

# Specifies the `pidfile` that Puma will use.
pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }

# Specifies the number of `workers` to boot in clustered mode.
# Workers are forked webserver processes. If using threads and workers together
# the concurrency of the application would be max `threads` * `workers`.
# Workers do not work on JRuby or Windows (both of which do not support
# processes).
#
# workers ENV.fetch("WEB_CONCURRENCY") { 2 }

# Use the `preload_app!` method when specifying a `workers` number.
# This directive tells Puma to first boot the application and load code
# before forking the application. This takes advantage of Copy On Write
# process behavior so workers use less memory.
#
# preload_app!

# Allow puma to be restarted by `rails restart` command.
plugin :tmp_restart

before_fork { Rails.configuration.split_factory.stop! }

on_worker_boot { Rails.configuration.split_factory.resume! }

# experimental puma cluster mode flags
# https://github.com/puma/puma/blob/1840014e77c9c0b2e9e094135396c25dd3c1df52/5.0-Upgrade.md
wait_for_less_busy_worker 0.001
nakayoshi_fork

If I change the rule of a split in split.io dashboard, the SDK syncs configuration change with split.io website in Puma process, but in Rails console, it seems the SDK client never syncs and the rule in memory is stale.

I've created a dedicated repo to illustrate this phenomenon. The repo is https://github.com/crypt0leslie/poc-test-split-io

Procedures to reproduce are described in the README.md of this repo.

Force Excluded vs Control

When a split/treatment is not defined or worse, when things go bad and Redis is down... control is the default treatment returned. For us, it made more sense to do excluded since this is an A/B throw away group convention. See: http://www.databoxdigital.com/2013/09/12/a-common-AB-testing-mistake/

To achieve these results, our wrapper gem for splitclient-rb makes the following change.

if Kernel.respond_to?(:silence_warnings) # Ruby 2.4 and up.
  silence_warnings do
    SplitIoClient::Engine::Models::Treatment::CONTROL = 'excluded'.freeze
  end
else
  begin
    old_verbose = $VERBOSE
    $VERBOSE = nil
    SplitIoClient::Engine::Models::Treatment::CONTROL = 'excluded'.freeze
  ensure
    $VERBOSE = old_verbose
  end
end

This is not an issue, just a communication that I thought it was really cool this gem used a constant which allowed us to freedom patch some things. Just wanted to share this and see if it solicited any thoughts or feedback. Thanks!

HTTP requests failing when faraday_middleware version < 0.10

While configuring splitclient-rb for an existing application, I've bootstrap SplitIoClient following the available documention:

require 'splitclient-rb'

options = {block_until_ready: 3}
split_factory = SplitIoClient::SplitFactoryBuilder.build("YOUR_API_KEY", options)

when instatiating the client I'm getting the following error:

ERROR -- : Failed to make a http request
INFO -- : Starting metrics service
ERROR -- : [splitclient-rb] Unexpected exception in store_splits: #<NoMethodError: undefined method `[]' for nil:NilClass> undefined method `[]' for nil:NilClass
	/bundle/gems/splitclient-rb-4.1.0/lib/cache/stores/split_store.rb:45:in `store_splits'
	/bundle/gems/splitclient-rb-4.1.0/lib/cache/stores/split_store.rb:35:in `block (2 levels) in splits_thread'
	/bundle/gems/splitclient-rb-4.1.0/lib/cache/stores/split_store.rb:34:in `loop'
	/bundle/gems/splitclient-rb-4.1.0/lib/cache/stores/split_store.rb:34:in `block in splits_thread'
INFO -- : Starting impressions service
WARN -- : uninitialized constant FaradayMiddleware::Gzip

If you look at the last line of the log, a warning is raised by Faraday saying that it doesn't know what is FaradayMiddleware::Gzip. NOTE: if you don't provide options to SplitIoClient::SplitFactoryBuilder.build, when instantiating the client the request get stuck and we need to manually terminate the application execution.

After looking at https://github.com/splitio/ruby-client/blob/master/lib/engine/api/client.rb#L10, I saw that we are instantiating Faraday with FaradayMiddleware::Gzip, which is a dependency defined in faraday_middleware gem.

After digging my code I discovered that I already have installed faraday_middleware in version 0.9.2. The issue is that FaradayMiddleware::Gzip is only available from version >= 0.10 https://github.com/lostisland/faraday_middleware/releases/tag/v0.10.0.

However, the currently .gemspec is not specifying a minor version for faraday_middleware, and if you already have a version installed, the gem will use that one, not matter which is the version of the installed gem. I will open a small PR to update .gemspec to specify a minor version for faraday_middleware as soon as possible 😄

Stream Event Parsing Silently Failing and Not Falling Back to Polling

Streaming events have been failing silently with JSON::ParserError: Empty input (after ) at line 1, column 1 when trying to parse a streaming event containing a payload missing a leading curly brace. The client did not automatically fallback to polling. The errors were only logged in debug mode and so we weren't alerted to the issue in our production logs. This led to large delays (>30min) from the time a flag definition was updated to the time our application would pick up the change. Only new deploys of the application would pick up new changes.

We are on v8.2.0 of the SDK.

This is happening here

Example payload missing leading curly brace:

'data:{"id":"6FMuBkchWr:0:0","timestamp":1710261542842, ... data":"{\"type\":\"SPLIT_UPDATE\", ...

JRuby support

I'm using the ruby client gem in one of our projects at Talkdesk and when running on JRuby an error is thrown due to a dependency using C extensions:

Just wondering if this is a known limitation and if there is value in being compatible with JRuby?

Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

current directory:
/usr/local/bundle/gems/digest-murmurhash-1.1.1/ext/digest/murmurhash
/opt/jruby/bin/jruby -r ./siteconf20180209-5-1h8ixdd.rb extconf.rb
creating Makefile

current directory:
/usr/local/bundle/gems/digest-murmurhash-1.1.1/ext/digest/murmurhash
make "DESTDIR=" clean

current directory:
/usr/local/bundle/gems/digest-murmurhash-1.1.1/ext/digest/murmurhash
make "DESTDIR="
make: *** No rule to make target '/opt/jruby/lib/ruby/include/ruby/ruby.h',
needed by 'neutral2.o'.  Stop.

make failed, exit code 2

Gem files will remain installed in
/usr/local/bundle/gems/digest-murmurhash-1.1.1 for inspection.

Split logger errors

I am receiving below error in my logger frequently:

Splitio: Error during connecting to streaming.split.io. Error: #<Socketry::TimeoutError: connection to 18.66.248.99:443 timed out>

Splitio: #<Socketry::TimeoutError: read timed out after 70 seconds>

Splitio: #<RuntimeError: eof exception>

Are these any concern?

Redis Availability

When we tested Split.io in standalone mode, I believe Redis going down caused get_treatment to not raise exceptions and return excluded. I could be wrong about this because as of now, we run the Go synchronizer and hence the Ruby apps are in consumer mode. Under this mode when the Redis connection goes down, any page that uses get_treatment will error out.

Our wrapper gem solves this by rescuing Redis::BaseError and returning SplitIoClient::Engine::Models::Treatment::CONTROL. To be fair, we never really had Redis go down and we do have our Split Redis AWS instance setup in such a way that we feel pretty confident about it being there for us. That said, just wanted to share this pattern and ask if there was anything more we could do on our end. Thanks in advance!

Quickstart: SDK Setup code does not work

I am new to Ruby and to Split, although, the code that was generated on the Quickstart: SDK Setup has an issue. The code is below:

require 'splitclient-rb'

options = {
  block_until_ready: 10
}

factory = SplitIoClient::SplitFactoryBuilder.build(API_KEY, options)
split_client = factory.client

treatment = split_client.get_treatment('CUSTOMER_ID', 'test')

if treatment == 'on'
  puts 'I am on'
elsif treatment == 'off'
  puts 'I am off'
else
  puts 'Control'
end

Independently of the block_until_ready value, I have increased it to 120, I always got ERROR -- : get_treatment: the SDK is not ready, the operation cannot be executed.

Solved it with split_client.block_until_ready but then I remove the instruction and afterward, I did a while loop to check how much time it would take to establish the connection:

while 1 do
treatment = split_client.get_treatment('CUSTOMER_ID', 'test')
if treatment == 'on'
  puts 'I am on'
elsif treatment == 'off'
  puts 'I am off'
else
  puts 'Control'
end
sleep(1)
end

And it would work after 1s, so I conclude that options with the block_until_ready is doing nothing.
Now I am not sure if it is a bug or if it is only the generated tutorial code that is wrong.
I am using splitclient-rb (7.2.2).

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.