Giter VIP home page Giter VIP logo

confex's Introduction

Confex

Inline docs Hex.pm Downloads Latest Version License Build Status Coverage Status Ebert

Confex simplifies reading configuration at run-time with adapter-based system for fetch values from any source. It's inspired by Phoenix {:system, value} definition for HTTP port and have no external dependencies.

Installation

It's available on hex.pm and can be installed as project dependency:

  1. Add confex to your list of dependencies in mix.exs:

    def deps do
      [{:confex, "~> 3.5.0"}]
    end
  2. Ensure confex is started before your application either by adding it to applications list as shown below or by making sure you use extra_applications option instead of applications (this feature is available since Elixir 1.4 and enabled by default for new projects):

    def application do
      [applications: [:confex]]
    end

Usage

  1. Replace values with configuration tuples

    Define configuration in your config.exs:

    config :my_app, MyApp.MyQueue,
      queue: [
        name:        {:system, "OUT_QUEUE_NAME", "MyQueueOut"},
        error_name:  {:system, "OUT_ERROR_QUEUE_NAME", "MyQueueOut.Errors"},
        routing_key: {:system, "OUT_ROUTING_KEY", ""},
        durable:     {:system, "OUT_DURABLE", false},
        port:        {:system, :integer, "OUT_PORT", 1234},
      ]

    Configuration tuples examples:

    • var - any bare values will be left as-is.
    • {:system, "ENV_NAME", "default"} - read string from system environment or fallback to "default" if it is not set.
    • {:system, "ENV_NAME"} - same as above, but raise error if ENV_NAME is not set.

    Additionally you can cast string values to common types:

    • {:system, :string, "ENV_NAME", "default"} (string is a default type).
    • {:system, :string, "ENV_NAME"}.
    • {:system, :integer, "ENV_NAME", 123}.
    • {:system, :integer, "ENV_NAME"}.
    • {:system, :float, "ENV_NAME", 123.5}.
    • {:system, :float, "ENV_NAME"}.
    • {:system, :boolean, "ENV_NAME", true}.
    • {:system, :boolean, "ENV_NAME"}.
    • {:system, :atom, "ENV_NAME"}.
    • {:system, :atom, "ENV_NAME", :default}.
    • {:system, :module, "ENV_NAME"}.
    • {:system, :module, "ENV_NAME", MyDefault}.
    • {:system, :list, "ENV_NAME"}.
    • {:system, :list, "ENV_NAME", ["a", "b", "c"]}.
    • {:system, :charlist, "ENV_NAME"}.
    • {:system, :charlist, "ENV_NAME", 'default'}.

    :system can be replaced with a {:via, adapter} tuple, where adapter is a module that implements Confex.Adapter behaviour.

    Type can be replaced with {module, function, arguments} tuple, in this case Confex will use external function to resolve the type. Function must returns either {:ok, value} or {:error, reason :: String.t} tuple.

  2. Read configuration by replacing Application.fetch_env/2, Application.fetch_env!/2 and Application.get_env/3 calls with Confex functions

    Fetch string values:

    iex> Confex.fetch_env(:myapp, MyKey)
    {:ok, "abc"}

    Fetch integer values:

    iex> Confex.fetch_env(:myapp, MyIntKey)
    {:ok, 123}

    Fetch configuration from maps or keywords:

    iex> Confex.fetch_env(:myapp, MyIntKey)
    {:ok, [a: 123, b: "abc"]}

Integrating with Ecto

Ecto.Repo has a init/2 callback, you can use it with Confex to read environment variables. We used to have all our repos to look like this:

defmodule MyApp do
  use Ecto.Repo, otp_app: :my_app

  @doc """
  Dynamically loads the repository configuration from the environment variables.
  """
  def init(_, config) do
    url = System.get_env("DATABASE_URL")
    config = if url, do: [url: url] ++ config, else: Confex.Resolver.resolve!(config)

    unless config[:database] do
      raise "Set DB_NAME environment variable!"
    end

    {:ok, config}
  end
end

Integrating with Phoenix

Same for Phoenix, use init/2 callback of Phoenix.Endpoint:

defmodule MyApp.Web.Endpoint do

  # Some code here

  @doc """
  Dynamically loads configuration from the system environment
  on startup.

  It receives the endpoint configuration from the config files
  and must return the updated configuration.
  """
  def init(_type, config) do
    {:ok, config} = Confex.Resolver.resolve(config)

    unless config[:secret_key_base] do
      raise "Set SECRET_KEY environment variable!"
    end

    {:ok, config}
  end
end

Populating configuration at start-time

In case you want to keep using Application.get_env/2 and other methods to keep accessing configuration, you can resolve it one-time when application is started:

defmodule MyApp do
  use Application

  def start(_type, _args) do
    # Replace Application environment with resolved values
    Confex.resolve_env!(:my_app)

    # ...
  end
end

However, don't drink too much Kool-Aid. Direct calls to the Confex are more explicit and should be default way to go, you don't want your colleagues to waste their time finding out how that resolved value got into the configuration, right?

Using Confex macros

Confex is supplied with helper macros that allow to attach configuration to specific modules of your application.

defmodule Connection do
  use Confex, otp_app: :myapp
end

It will add config/0 function to Connection module that reads configuration at run-time for :myapp OTP application with key Connection.

You can add defaults by extending macro options:

defmodule Connection do
  use Confex,
    otp_app: :myapp,
    some_value: {:system, "ENV_NAME", "this_will_be_default value"}
end

If application environment contains values in Keyword or Map structs, default values will be recursively merged with application configuration.

We recommend to avoid using tuples without default values in this case, since config/0 calls will raise exceptions if they are not resolved.

You can validate configuration by overriding validate_config!/1 function, which will receive configuration and must return it back to caller function. It will be evaluated each time config/1 is called.

defmodule Connection do
  use Confex, otp_app: :myapp

  def validate_config!(config) do
    unless config[:password] do
      raise "Password is not set!"
    end

    config
  end
end

Adapters

Currently Confex supports two embedded adapters:

  • :system - read configuration from system environment;
  • :system_file - read file path from system environment and read configuration from this file. Useful when you want to resolve Docker, Swarm or Kubernetes secrets that are stored in files.

You can create adapter by implementing Confex.Adapter behaviour with your own logic.

Helpful links

confex's People

Contributors

adzz avatar alexfilatov avatar andrewdryga avatar arthur29 avatar bernardd avatar gmile avatar kabie avatar kbaird avatar kianmeng avatar mbaeuerle avatar menedev avatar pavelvesnin avatar sebastianpoeplau avatar take-five avatar trenpixster avatar webdeb avatar zachdaniel avatar zorbash 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  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

confex's Issues

RFC: Address compile/runtime issues in raised on Elixir Forum

Confex already provides you a way to have a compile-time config and delegate resolution of it's part to runtime. This delegation is already very explicit even though it has a downside - values with :system tuples can have interference with values set by other libraries.

So what are we trying to solve:

  1. Provide a way to separate compile and runtime configuration;
  2. It should be as simple as possible and require developers to minimize changes to adopt them. People should not look into multiple files to figure out where some variable came from;
  3. If it's not adopted, old way should keep working (probably issuing warnings);
  4. Releases (we should have them though, but it's a separate work).

What we should not solve here:

  1. Generalization of where runtime configuration lives (closer to code or in config files). Having it closer to code is always an option and if community thinks it's the way to go - it's more educational work rather than techical.

To further extend it:

  • We should not write to Application environment at all, instead, we can think of it as a compile-time configuration where system tuples are arbitrary values that tell us how to resolve their runtime value. (We would need to deprecate Confex.resolve_env!.)

  • Users do not always expect performance hit added by Confex callbacks that resolve the value each time it's called, a new cache layer should be added that resolves them only once and stored in a public ETS table (similar to how Application env work). It can be placed in Confex.RuntimeEnv module, which mimics Application.*_env behaviour. We may add a function to populate the app when it's started though.

  • Because configuration can change at runtime we should though how that caching works:

    1. When Application env is changed (somebody can write a new :system elsewhere);
    2. When new code is released (hot code upgrade);
    3. When the value in external API is changed (probably we need a watch/2 optional callback for Confex adapters).
  • There are some edge cases when configuration change should trigger other action in the application, the best example I come up if Vault that rotates PostgreSQL password. So when password value is changed, there should be a code that subscribed to that change and gracefully restarts the connection pool.

  • Prepare showcase (probably with macros similar to Mix.Config) how it would work altogether.

This can be merged with some ideas from Jose RFC:

  • config_paths to get rid off import_config/1;
  • Application.Config can hold the macroses and Application[.Runtime] can become this runtime config "caching" layer;

Some more points from discussion and my own experience:

  • apps in umbrella should not have their own configuration by default, even though it should be possible to do if people want to cut off multiple releases from a single app.

Link to the discussion: https://elixirforum.com/t/proposal-moving-towards-discoverable-config-files/14302/73

No default and nil as default aren't the same

Maybe I am wrong, but this doesn't feel to be the truth:

* `{:system, :integer, "ENV_NAME"}` - same as `{:system, :integer, "ENV_NAME", nil}`;

For me, I get an ArgumentError when using {:system, :integer, "ENV_NAME"}:

** (Mix) Could not start application example: exited in: Example.Application.start(:normal, [])
    ** (EXIT) an exception was raised:
        ** (ArgumentError) can't fetch value for key `Example.Config` of application `:example`, can not resolve key ENV_NAME value via adapter Elixir.Confex.Adapters.SystemEnvironment

But it works fine when using {:system, :integer, "ENV_NAME", nil}. So, it is not the same.

Unable to read config from file

hi there,

Not sure if this is a beginner error but I'm to able to read in a config from a file when using the below line
date: {:system_file, "DATE_CONFIG"}

I have also set and passed in DATE_CONFIG into docker during build time and have verified it by doing RUN echo $DATE_CONFIG

I added a debug statement so I can see the passed in date during compilation. I added IO.puts("date here: #{@date}") in one of the files and I just see the path that's set in the env var not the value of the file.

For additional context, the file I'm trying to read is built into the docker image and is located in /assets/... and not within the project directory. I'm also on Confex 3.3.1.

Am I doing something wrong here?

Thanks in advance!

Edit: I'm also new to Elixir so I may totally be doing something wrong here.

Open to new feature PR?

Are you guys open to a PR for giving a helpful error with the name of the required ENV value?

Example:

config :core, :test_required_int, {:system, :integer, "TEST_REQUIRED_INT"}

Now, when the ENV variable is NOT SET, in a console:

> Confex.get(:core, :test_required_int)
** (MatchError) no match of right hand side value: :error
    (confex) lib/confex.ex:147: Confex.cast/2
    (confex) lib/confex.ex:112: Confex.get_value/1
    (confex) lib/confex.ex:36: Confex.get/3

I would like to update the library to treat that as required and give a helpful error message like "ENV setting 'TEST_REQUIRED_INT' is required".

Then if the value exists but can't be parsed to an integer, I get the same error message as above. I would prefer to get an error like "ENV setting 'TEST_REQUIRED_INT' value error: "abc" failed to convert to an integer".

Are you guys open to receiving this type of a feature?

When we have front-end developers running a local backend, if they are missing or have badly configured environment settings, using both System.get_env and the current way Confex works give no help in figuring out which, of the many settings available, has a problem.

Confex AWS Parameter store plugin

Hi all, we are using Confex in a project where we also wanted to pull config from the AWS parameter store, I put together a Parameter Store plugin for Confex[1] so we could keep resolving both :system and our plugin using the same code, also Confex has been very helpful in general so I hoped we might contribute somewhat :)

I'm very interested in any feedback from contributors to Confex regarding the plugin, there is certainly room for improvement. The original version we use internally does include a cacheing mechanism by default as we do have places where params are requested repeatedly, however I did not want to make that a default behaviour as it introduces state into the plugin, cacheing should be explicitly requested by the user imo.

[1] https://github.com/gpedic/confex_parameter_store

Support atoms as values

Right now it's impossible to express atom as a value, Confex will perceive it as a string.

Example:

MY_ENV=MyModuleName iex -S mix

In the example above MyModuleName can only be string:

config :my_app, my_var: {:system, :string, MyModuleName, "my_default_value"}

To support the case above :atom must be added to a list of supported types.

Nested data structs are not resolved as expected

An OTP app config with nested data structs evaluates to nil if at least one entry specifies system env var that is not set.

Example:

config :my_app,
  var: [
    foo: {:system, :string, "FOO"},
    bar: "bar-value"
  ]

Expected:

iex> Confex.get_env(:my_app, :var)

[
  foo: nil,
  bar: "bar-value"
]

Less expected, but still meaningful:

iex> Confex.get_env(:my_app, :var)

[
  bar: "bar-value"
]

Actual:

iex> Confex.get_env(:my_app, :var)

nil

Workaround:

Use 4 element tuple with a default value of nil

config :my_app,
  var: [
    foo: {:system, :string, "FOO", nil},
    bar: "bar-value"
  ]

####

iex> Confex.get_env(:my_app, :var)

[
  foo: nil,
  bar: "bar-value"
]

Confex in complex structures

Hello,

Today I needed to use confex to resolve some structures like tuples and lists with different forms. For example:

{{:system, :string, "SOME_VAR", "some_value"}, "some_value2", "some_value3"}
[{:system, :string, "SOME_VAR", "some_value"}, "some_value2", "some_value3"]

I was reading the tests and saw two of them were giving guarantees that confex doesn`t resolve this type of structures.

test "does not resolve in tuples" do
  assert Resolver.resolve({1, 2, 3, {:system, "KEEP_THIS"}}) == {:ok, {1, 2, 3, {:system, "KEEP_THIS"}}}
end
test "does not resolve in lists" do
  assert Resolver.resolve([1, 2, 3, {:system, "KEEP_THIS"}]) == {:ok, [1, 2, 3, {:system, "KEEP_THIS"}]}
end

Why confex does not resolve those kinds of structures?

Thanks,
Arthur

More flexible adapter interface

Hi there! I was just looking at implementing an adapter for AWS Secrets Manager, and hit an issue (that I'd already encountered when writing my Vault one but decided to just ignore). The current adapter interface is a simple String.t() -> String.t() mapping, which is fine for environment variables but both vault and SM have names for both the secret and the values therein. So to fully describe the "key" for a value being pulled from them you need both the secret name and the key within the secret. I can think of a few ways of approaching this:

  • Do what I did in ConfexVault and just force the user to always use the key value or similar within the secret. In this instance you can still declare the value as {{:via, MyAdapter}, "some_name"}, but you're restricted in how you use the store itself.
  • Squish the secret name and key together and let the adapter prise them apart, meaning you had something like {{:via, MyAdapter}, "secret_name/secret_key"}.
  • Extend the adapter behaviour so that it can take a more flexible input term, say tuple(String.t()) | String.t() -> String.t(). Then you could do something like {{:via, MyAdapter}, {"secret_name", "secret_key"}}

The first two feel kind of half-baked, but the third would obviously require some changes to Confex itself, even if only to the behaviour's typespec.

I'd be really interested in getting your thoughts on the best way to approach this.

`get_env/2` inconsistent with `Application` for type range

It seems that Confex.get_env errors when the config value is set to a range.

Replicate

  1. Configure a variable to be of type range.
config :test_app,
  range: 1..5,
  ...
  1. Open app with iex.
$ iex -S mix
  1. See Application behavior (desired).
iex(1)> Application.get_env(:jessie, :range)
1..5
  1. See Confex behavior.
iex(4)> Confex.get_env(:jessie, :range)
** (FunctionClauseError) no function clause matching in Confex.Resolver.reduce_map/2

    The following arguments were given to Confex.Resolver.reduce_map/2:

        # 1
        1

        # 2
        %{}

    Attempted function clauses (showing 4 out of 4):

        defp reduce_map({key, nil}, acc)
        defp reduce_map({key, list}, acc) when is_list(list)
        defp reduce_map({key, map}, acc) when is_map(map)
        defp reduce_map({key, value}, acc)

    (confex) lib/confex/resolver.ex:61: Confex.Resolver.reduce_map/2
    (elixir) lib/range.ex:93: Enumerable.Range.reduce/5
    (elixir) lib/enum.ex:1872: Enum.reduce_while/3
    (confex) lib/confex/resolver.ex:41: Confex.Resolver.resolve/1
    (confex) lib/confex.ex:199: Confex.get_env/3

Error

It seems that the error here stems from the range type not being equivalent to a list. Because of this, it doesn't match any of the function headers because is_list(1..n) returns false.

Usage Patch

For those blocked by this, a patch we've found that converting the range to a list (which is supported) with Enum.to_list/1 is a simple enough fix that allows us to avoid mixing Confex and Application usage. We don't have a many ranges in our configs, but if we did, this patch would be less than ideal.

Example

# replace this: 
config :test_app, range: 1..5 # errors

# with this:
config :test_app, range: Enum.to_list(1..5)

Unable to follow directions and get basic working example

I'm very interested in your package. I have similar needs and started to build my own solution. I would prefer to use your package, however, I'm having trouble following how to actually use it.

The readme mentions:

Define your configuration in config.ex of your application.

I'm not sure if that's a typo (config.exs) or if you are suggesting to create config.ex file that gets loaded by the application in a callback or something. If so, can you provide a sample?

I added the config example (even though I'm not using AMQP) to my config.exs file and tried playing with getting values throuh iex -S mix terminal.

This is what I get:

Confex.get(:core, AssetProcessor.AMQP.Producer)
[queue: [name: {:system, "OUT_QUEUE_NAME", "MyQueueOut"},
  error_name: {:system, "OUT_ERROR_QUEUE_NAME", "MyQueueOut.Errors"},
  routing_key: {:system, "OUT_ROUTING_KEY", ""},
  durable: {:system, "OUT_DURABLE", false},
  port: {:system, :integer, "OUT_PORT", 1234}]]

I expected values instead if the {:system, _, _} tuple. I get the same thing using Application.get_env.

Can you please point me to a guide, example, blog post, or something that shows how to get started with the library? I feel like I'm just missing some small but critical piece of information.

proposal: resolve envs in runtime

Hello,

I've used cofex recently and despite its both clean and straightforward approach strengths, I'm missing something that could boost even more elixir developers lives when doing configs.

Problem

Need to manually resolve configs for every application on your mix project.

Motivation

There's an issue with the manual approach me and my team stumbled on. When using confex for logger application, for instance, logger is resolved before even the project application being executed and for consequence Confex.resolve_env(:logger) being called.

Proposal

Some way to get mix projects configs resolved at runtime, without the need of manually Confex.resolve_env(:app) for each :app.

  1. Create a Confex.Application that resolves all configs for all project applications
  2. Make runtime resolving optional through config e.g.: config :confex, resolver: :runtime
  3. Create docs to start confex as an extra_application when resolving configs at runtime. (Only this step would solve the :logger config issue) e.g.:
  def application do
    [
      mod: {Foo.Application, []},
      extra_applications: [:confex, :logger]
    ]
  end

  defp deps do
    [
      {:confex, "~> 3.3.1", optional: true}
    ]
  end

Thanks in advance for reading, and if you think this change is a good idea, I can personaly help implemeting it, since everthing written in the proposal has been tested and implemented already.

Best,
Math

Possible to use with Ecto?

When I am building a release, how can I use confex to define the DB credentials from within the environment? I mean the environment, from where I am running the release not building.

Well, this would be the nice to have case :)

config :app, App.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: {:system, "PG_USER"},
  password: {:system, "PG_PASS"},
  hostname: {:system, "PG_HOST"},
  database: {:system, "PG_DB"},
  pool_size: 15

Thanks!

Lists of lists of config

Hey there,
I have a case where want to resolve system variables in a nested list (specifically it's config for Redlock - see the example at https://github.com/lyokato/redlock#single-node-mode).

The simplest example is something like:

config :my_app,
  servers: [
    [
      host: {:system, :string, "SERVER_HOST"},
      port: {:system, :integer, "SERVER_PORT"}
    ]
  ]

Looking at the test at resolver_test:90, you evidently don't want list elements to resolve under certain situations (I'm not 100% clear on why, but I'm sure there's a good reason), and this case seems to fall under that non-resolving class. I'd suggest, though, that the case above seems like a pretty reasonable one to have resolving (or, otherwise, I'd love to hear if there's an alternative way to go about it).

Cheers

config() returns nil when resolving configuration fails

The comment https://github.com/Nebo15/confex/blob/master/lib/confex.ex#L314 says that ArgumentError is raised when configuration cannot be resolved. This however is not true.

Here you can see the configuration:

iex(1)> {:ok, c} = Application.fetch_env(:transporter, Transporter.ProxyRequests.Process)
{:ok,
 [
   bypass: %{
     ~r/\/\/client-logger\..*/ => %{
       host: {:system, "CLIENT_LOGGER_SERVICE_HOST"},
       port: 80,
       scheme: "http"
     }
   }
 ]}

CLIENT_LOGGER_SERVICE_HOST env var is not actually set.

If I use use Confex, otp_app: :transporter macro, then Transporter.ProxyRequests.Process.config() returns just nil.

I think this is because Confex is using get_env https://github.com/Nebo15/confex/blob/master/lib/confex.ex#L319 which defaults to nil on error.

Feel free to close this ticket if this behavior is expected. It caused us a small incident because one of the env vars was missing and it didn't fail fast.

Credo lint for compile time variables

My company is trying to blacklist compile time variables. To do so, I am creating a credo linter to fail our pipelines if any are found. Currently I am only linting for calls to System.get_env. Is there other calls I should be looking for?

In case anyone else is interested in the linter, here is the source and the hex.

Question on differences from Application.get_env

I wanted to bring up this detail that differs from Application.get_env. Let me just jump into the example I have here:
My config block: https://github.com/techgaun/confex-sample/blob/86a50a2a81d226235987fa7d579891c2bab27e18/config/config.exs#L32-L36

My test block: https://github.com/techgaun/confex-sample/blob/86a50a2a81d226235987fa7d579891c2bab27e18/test/confex_sample_test.exs#L5-L10

The assertion fails as:

1) test Application.get_env & Confex.get_env difference (ConfexSampleTest)
     test/confex_sample_test.exs:5
     Assertion with === failed
     code:  assert app_conf === confex_conf
     left:  [list: [name: {:system, "CONFEX_MY_NAME"}, address: "Earth"]]
     right: nil
     stacktrace:
       test/confex_sample_test.exs:9: (test)

As you can see, if any of the parameters is not resolved there, the nil is returned instead for the entire list. Its probably by design but at least as an opt-in, it would be nice to get either of the following:

# unresolved plain
 [list: [name: {:system, "CONFEX_MY_NAME"}, address: "Earth"]]

# resolved but with :error
 [list: [name: :error, address: "Earth"]]

Arrays not works

Hello!
values are not substituted into arrays

config :bots, conftest3:
[
%{
key: {:system, :string, "PARAM1", "some value"}
},
....
]

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.