beam-community / ex_machina Goto Github PK
View Code? Open in Web Editor NEWCreate test data for Elixir applications
Home Page: https://hex.pm/packages/ex_machina
License: MIT License
Create test data for Elixir applications
Home Page: https://hex.pm/packages/ex_machina
License: MIT License
Thanks so much for making this! I'm very new to Elixir and Phoenix, so I might be wrong. But I discovered something today that makes me think that factories made with the create
function are not being wrapped in changesets before hitting the db.
I have a User model with the following changeset definition. The model also has a null: false
constraint on encrypted password
def changeset(model, params \\ :empty) do
model
|> cast(params, @required_fields, @optional_fields)
|> validate_length(:email, min: 1)
|> validate_length(:password, min: 1)
|> validate_confirmation(:password, message: "Password does not match")
|> unique_constraint(:email, message: "Email has already been taken")
|> generate_encrypted_password
end
defp generate_encrypted_password(changeset) do
pass = changeset.params["password"]
put_change(changeset, :encrypted_password, Comeonin.Bcrypt.hashpwsalt(pass))
end
The User factory:
def factory(:user) do
%User{email: "[email protected]", password: "password",
password_confirmation: "password"}
end
And a test to test for unique emails:
test "unique emails" do
user = create(:user)
changeset = User.changeset(user, %{email: user.email, password: "password",
password_confirmation: "password"})
refute changeset.valid?
end
When I run this test, I get a not_null_violation
as the encrypted password field is blank. Dropping IEx.pry
into my changeset function indicates the changeset function (and generate_encrypted_password
) are not being called by Ex Machina. I tried this for other models in my app with the same result.
When I make a record normally, by creating a changeset then calling Repo.insert
things work fine.
The source for ExMachina.Ecto.save_record
doesn't appear to use a changeset, though I might be misreading this
https://github.com/thoughtbot/ex_machina/blob/master/lib/ex_machina/ecto.ex#L93
Embeds can only be manipulated via changesets, be it on insert, update or delete.
I've found this useful for asserting the validity of my factories when working in Ruby, and think it would be nice in Elixir too. :)
https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#linting-factories
Recently I had to create a lot of events that all had the same user_id
user_id = 123
create(:event, name: "cancelled", user_id: user_id)
create(:event, name: "started exercise", user_id: user_id)
create(:event, name: "finished exercise", user_id: user_id)
I think it would be nice to have a function that allows you do to something like this
ExMachina.with(user_id: user_id) do
create(:event, name: "cancelled")
create(:event, name: "started exercise")
end
I'm not exactly sure how this would work, or if this is the syntax/naming we want, but I think we could come up with something
Instead of having a separate save_record
function we could probably just make it the create
function and have a default create that raises, similar to what we already do for save_record
. I think this is easier to understand conceptually. You add a create
function to handle create
calls. Pretty simple
This will make it so you can use it for things like usernames more easily
sequence("username")
Elixir will raise an error if you try to pass an unknown key when creating a struct like so:
iex(2)> %Foo{invalid: "invalid"}
** (CompileError) iex:3: unknown key :invalid for struct Foo
While not every ExMachina factory generates a struct, some do. When we use build
, we convert the passed data to a map which doesn't raise errors for invalid keys. Would it make sense to look for a __struct__
field, and if it's there, try to convert it to a struct to raise any errors?
Elixir 1.2 introduced struct!
which might help.
Is there any other way of doing this?
The breaking change is is Ecto.Association.loaded?
is no longer a function, but they've added Ecto.assoc_loaded?
as a public function.
There are some deprecation warnings too from changes in the handling of assoc, embed, etc inside of changesets is different now and causes warnings in ex_machina right now.
.warning: changing assocs with change/2 or put_change/3 is deprecated, please use put_assoc/3 instead
(stdlib) lists.erl:1262: :lists.foldl/3
(ecto) lib/ecto/changeset.ex:247: Ecto.Changeset.change/2
(ex_machina) lib/ex_machina/ecto.ex:128: ExMachina.Ecto.save_record/3
test/ex_machina/ecto_has_many_test.exs:77: ExMachina.EctoHasManyTest."test create/2 saves overriden `has_many` associations"/1
.warning: changing assocs with change/2 or put_change/3 is deprecated, please use put_assoc/3 instead
(stdlib) lists.erl:1262: :lists.foldl/3
(ecto) lib/ecto/changeset.ex:247: Ecto.Changeset.change/2
(ex_machina) lib/ex_machina/ecto.ex:128: ExMachina.Ecto.save_record/3
test/ex_machina/ecto_has_many_test.exs:85: ExMachina.EctoHasManyTest."test create/1 creates a record without any associations"/1
.warning: changing assocs with change/2 or put_change/3 is deprecated, please use put_assoc/3 instead
(stdlib) lists.erl:1262: :lists.foldl/3
(ecto) lib/ecto/changeset.ex:247: Ecto.Changeset.change/2
(ex_machina) lib/ex_machina/ecto.ex:128: ExMachina.Ecto.save_record/3
test/ex_machina/ecto_has_many_test.exs:67: ExMachina.EctoHasManyTest."test create/1 saves `has_many` records defined in the factory"/1
.warning: changing assocs with change/2 or put_change/3 is deprecated, please use put_assoc/3 instead
(stdlib) lists.erl:1262: :lists.foldl/3
(ecto) lib/ecto/changeset.ex:247: Ecto.Changeset.change/2
(ex_machina) lib/ex_machina/ecto.ex:128: ExMachina.Ecto.save_record/3
(ex_machina) lib/ex_machina/ecto.ex:142: anonymous fn/3 in ExMachina.Ecto.persist_belongs_to_associations/2
.warning: changing embeds with change/2 or put_change/3 is deprecated, please use put_embed/3 instead
(stdlib) lists.erl:1262: :lists.foldl/3
(ecto) lib/ecto/changeset.ex:247: Ecto.Changeset.change/2
(ex_machina) lib/ex_machina/ecto.ex:128: ExMachina.Ecto.save_record/3
test/ex_machina/ecto_embeds_test.exs:42: ExMachina.EctoEmbedsTest."test create/1 saves `embeds_one` record defined in the factory"/1
.warning: changing embeds with change/2 or put_change/3 is deprecated, please use put_embed/3 instead
(stdlib) lists.erl:1262: :lists.foldl/3
(ecto) lib/ecto/changeset.ex:247: Ecto.Changeset.change/2
(ex_machina) lib/ex_machina/ecto.ex:128: ExMachina.Ecto.save_record/3
test/ex_machina/ecto_embeds_test.exs:50: ExMachina.EctoEmbedsTest."test create/2 saves `embeds_one` record when overridden"/1
As request by Jose elixir-ecto/ecto#1102 (comment)
This should simplify the Ecto adapter significantly.
If we don't specify some fields, these fields are nil value on the map returned by params_for/2
.
Most of the time we don't want to put nil into database. So wy not just remove them ?
params
|> Enum.filter(fn {_, v} -> v != nil end)
|> Enum.into(%{})
I believe it's because the test is running async. If we then that off we should be fine. I'll tackle that tomorrow
Found this problem using params_for
, etc.
defp drop_ecto_fields(record = %{__struct__: struct, __meta__: %{__struct__: Ecto.Schema.Metadata}}) do
...
|> Map.drop(struct.__schema__(:primary_key))
In the simple case the primary key is a synthetic id that is auto-generated by the database. In the not so simple case, you have overridden the primary key so that it's not auto-generated by the database. It could be a business key that is meaningful. It could be a one-to-one mapping where the primary key is a foreign key reference to another table.
e.g.
# This has a normal auto-incrementing ID
schema "plays" do
has_one :scoring
end
# This is a one-to-one modeled in the database by having the PK be a FK to the plays.id column.
@primary_key {:play_id, :integer, []}
schema "scoring" do
field :type, :string
field :points, :integer
belongs_to :play, MyApp.Play, define_field: false, references: :id
belongs_to :team, MyApp.Team, references: :id
end
In the latter case, the primary key is also a foreign key reference to another table and is a required field, so using params_for
and friends creates invalid params since it drops the PK.
There is a function that throws this warning. I believe it is the save_record function because we overwrite it in the custom module, when calling use ExMachina.Ecto
we should figure out where that is, and how to get rid of the warning. Maybe with defoverridable?
I have a User model that hashes the user's password when you call User.changeset/2
(gist here). How do I create a factory that correctly hashes the password when I do the following?
MyApp.Factory.build(:user, password: "supersecret")
I'm new to elixir, so if there is a better approach for automatically hashing the password, please let me know.
The factory definition syntax right now uses the same function name, but pattern matches on the first argument. This works ok, but it does cause a few problems
I propose that we change the factory definition syntax to {factory_name}_factory. This will also make it so we can add callbacks that are grouped near the factory in the future
defmodule Factory do
# This is nice because you can add functions for the user factory near it instead of at the bottom
# Also works better with symbol view so it doesn't show a bunch of `factory` functions.
def user_factory do
%User{
first_name: "Paul",
last_name: "Smith",
}
end
# You can now define helper functions near the factory that it applies to
def make_admin(user) do
assing_to_admin_group(user)
end
# In the future we could add callbacks like this. If we keep the current syntax you'd have to put these
# at the bottom of the file which makes it hard to see what a factory can do
def after_build_user(user, attrs) do
email = attrs.first_name <> attrs.last_name <> "@foo.com"
Map.put_new(user, :email, email)
end
def comment_factory do
%Comment{
body: "This is my comment"
}
end
end
You would still call these with the same function build(:user)
, etc.
I had(?) to do this:
def factory(:package, _attrs) do
{:ok, time} = Ecto.DateTime.cast({{2001, 2, 3}, {4, 5, 6}})
%Package{
hex_updated_at: time,
}
Would be nice if you could just pass a string like "2001-02-03T04:05:06Z"
and it would be automatically cast.
Casting happens if you do
Repo.insert! Package.changeset(%Package{}, %{name: "my_package", description: "My package.", hex_updated_at: my_time_string})
for example.
I think we can do something similar to the message used in Phoenix views: https://github.com/phoenixframework/phoenix/blob/v1.0.2/lib/phoenix/view.ex#L145
I often use fields_for
when creating or updating records in API endpoints or for testing controllers. I think params_for
makes more sense in these cases.
post comment_path(conn, :create), params_for(:comment)
When app is growing and models amount is growing too, it's simpler just to have a bunch of files, one for each model, like rails factory_girl
gem does.
But it looks like this gem doesn't either ability to create factories in separate files and documentation. WDYT?
This is not a bug per se, but just something surprising that we ran into. Maybe it deserves some documentation? Mostly putting it here to get some feedback.
The sequence is a global Agent that stores the sequence globally for a given atom which was unexpected. The sequences for a given factory are not necessarily sequential. In general, depending on the sequences of a type to be sequential is probably bad of course.
This can cause problems though when the size of the field is limited and you have a lot of lookups, you exponentially grow that sequence and can't figure out why.
i.e. You have multiple models/tables that have some of the same columns
def factory(:lookup_type) do
%MyModule.LookupType{
abbreviation: sequence(:abbreviation, &"D#{&1}"),
name: sequence(:name, &"Name#{&1}"),
}
end
def factory(:another_lookup_type) do
%MyModule.AnotherLookupType{
abbreviation: sequence(:abbreviation, &"D#{&1}"),
name: sequence(:name, &"Name#{&1}"),
}
end
I think factory_girl works on a sequence per factory, I guess I expected ex_machina to be the same.
This will remove a lot of the code and duplicate logic between Ecto and ExMachina see #83
So it would be something like. It would save all has_many and belongs_to automatically
def save_record(repo, record) do
repo.insert!(record)
end
A lot of times I just want a unique value like this:
title: sequence(:title, fn(n) -> "Title #{n}" end)
body: sequence(:comment_body, fn(n) -> "Comment Body #{n}" end)
For simple cases like this I think it would be helpful if you could do something like
title: sequence(:title) #generates "Title #{n}"
body: sequence(:comment_body) #generates "Comment Body #{n}"
Thoughts on this? Would it be better to use Faker or would this be useful?
I've been thinking about this a lot. It seems attrs
can be confusing and tricky to debug (see #54 and #51). I wonder if it would be better to remove it and instead handle associations after build. I imagine it would look something like this:
def factory(:comment) do
%Comment{
body: "My Comment",
author: belongs_to(:author)
}
end
def belongs_to(key) do
%ExMachina.Ecto.BelongsTo{key: key}
end
Then in the build function it could iterate through the keys and if it comes along a %ExMachina.Ecto.BelongsTo
struct it would build one. I think this makes factory definition and use a lot more clear, and clean. We'd lose the ability to use attrs
inside the factory, but that causes bugs in a lot of cases anyway (#54 (comment))
So I think it's better to leave the factory definitions as clean and clear as possible and do the transformation later
When running mix test I see the following error:
** (ExMachina.UndefinedFactoryError) No factory defined for :user.
Please check for typos or define your factory:
def :user_factory do
...
end
Notice that it should be user_factory
, not :user_factory
.
What do y'all think about adding support for "lazy" attributes using functions?
I've got an implementation ready to go. This commit explains everything and includes some examples that should justify why I think this is useful:
Here's a little example:
def hero_factory do
%{
creature: "Bat",
nickname: &"#{&1.creature}man",
vehicle: &"#{&1.creature}mobile"
}
end
build(:hero)
# => %{creature: "Bat", nickname: "Batman", vehicle: "Batmobile"}
build(:hero, creature: "Spider", vehicle: "webs")
# => %{creature: "Spider", nickname: "Spiderman", vehicle: "webs"}
Should I open a pull request?
Thanks!
(Newish to elixir, so apologies in advance ๐)
The README says to add ex_machina
to the list of applications. Doesn't this mean that ex_machina
is started in all envs, not just the test? I've seen a few test-only libraries recommend adding a line to test_helper.exs
to start the app in tests only. Is this a different use-case?
fields_for(:post, comments: []) # Results in no goals key
fields_for(:post) |> Map.put(:comments, []) # Sets the results key correctly
Based on the issue by @geofflane in #104. I would love help on this if someone has time, if not I hope to tackle this in a few weeks.
params_with_assocs/2
to ExMachina.EctoExMachina.Ecto.params_for/2
belongs_to
relationships from the factory, but not insert the factory itself. e.g. if you call params_with_assocs(:comment)
it should insert the comment's belongs_to associations, but not the comment itself.Comment
belongs_to a Post
, params_with_assocs(:comment)
would insert the post and use the post's primary key and put on on the comment.params_for/2
works currently. https://github.com/thoughtbot/ex_machina/blob/f6285d2033933424d78870e70810acae8885e1fb/lib/ex_machina/ecto.ex#L75params_with_assocs(:comment, post: build(:post))
. If the post is built then it will insert it and set the appropriate foreign key. If the association has already been inserted it will not try to insert it again, and will set the appropriate foreign key.Hello,
Let's get one thing straight - FactoryGirl is a wicked and brittle: instead of encouraging PORO usage someone decided to take metaprogramming homework onto the next level and created a DSL that merely resemble the Rails story: it's cool for simple things but requires to learn a language inside a language to approach to the complexity of real-world applications. And that doesn't even guarantee to cover answer all the questions.
Why should developers spend time to learn a dialect if they already knows Ruby?
Too much of magic kills all the fun (beyond the certain point), and I believe you as a maintainers of the FC know that better then anyone else.
Please, do not repeat this story with Elixir, do not abuse metaprogramming - use it point-wise and stand by explicitness.
Thanks for all your work and contribution in open-source.
I was thinking about a way to do things after/before create/build or to do intersting things with the attrs if needed. I think it would be awesome to be able to define a custom function like so
def create(:article, attrs \\ []) do
do_create(:article, attrs) |> with_comment
end
def build(:user, attrs \\ []) do
email = attrs.first_name <> attrs.last_name <> "@foo.com"
do_build(:user, Map.put_new(attrs, email: email))
end
do_build
and do_create
would do what create/2
and build/2
do today, and create/build
would just call do_create
and do_build
with the passed in arguments. This would be an easier change and would allow for a very flexible build/create process
Similar to the one here: https://github.com/thoughtbot/ex_machina/blob/master/lib/ex_machina/ecto.ex#L18
There used to be an assoc macro and that doesn't currently raise a helpful message when people upgrade. I think it would be good to add one.
Somehing like: the assoc/2 macro has been removed. You can now use build(:factory_name) instead.
Maybe link to the associations section of the Readme as well
We added an error here: https://github.com/thoughtbot/ex_machina/blob/0.5.0/lib/ex_machina.ex#L212
but that will never be called since internally we are calling factory/1. Instead that error message should be added to https://github.com/thoughtbot/ex_machina/blob/0.5.0/lib/ex_machina.ex#L16 UndefinedFactory
. Something like:
"""
No factory defined for #{inspect factory_name}. This may be because you have defined a factory with attrs like this: def factory(:my_factory, attrs)
This has been changed in 0.5.0 to just be: def factory(:my_factory). The assoc/2 function has also been removed. Please define your factory function like this:
def factory(#{inspect factory_name}) do
...
end
"""
I think it would be good to add helpful errors when calling create, create_pair and create_list on ExMachina so people know that things have changed when they upgrade to 1.0.0
So when someone uses ExMachina with Ecto they know that they need at least Ecto 2.0
That way we know if Ecto is 1.1 or not. Raise if it's not
I'm trying to use ExMachina alongside Ecto to test some code with some models with lots of relations, but I keep running into an error where create() tries to insert a relation a second time.
Here's what my test code looks like so far:
def factory(:contact) do
%Contact{
}
end
def factory(:contact_profile) do
%ContactProfile{
city: "Plano",
region: "TX",
}
end
contact_profile = create(:contact_profile)
contact = create(:contact, contact_profile: contact_profile)
After the last line, I keep getting this error:
** (Ecto.ConstraintError) constraint error when attempting to insert model:
* unique: contact_profiles_pkey
As far as the model goes, the ContactProfile model has a belongs_to relationship with Contact, and Contact has a has_one relationship with ContactProfile.
I think this could be very useful for working with times or custom types
By default the Phoenix ConnCase imports Ecto.Model. This can lead to ambiguous calls to build
. I believe we may be able to solve this by using guards in our function. That ensure the first argument is an atom.
From the Kernel.SpecialForms docs
If two modules A and B are imported and they both contain a foo function with an arity of 1, an error is only emitted if an ambiguous call to foo/1 is actually made; that is, the errors are emitted lazily, not eagerly.
That way people can do things with the module. For example, you could create an Ecto strategy that let's you use a changeset like in #78
Also convert keyword lists to map so they part of the map can be matched. Keyword lists require a full match
defmodule ExMachina.EctoWithChangesetStrategy do
use ExMachina.Strategy, function_name: :insert
def handle_insert(record, %{repo: repo, factory_module: module, factory_name: factory_name}) do
changeset_function_name = to_string(factory_name) <> "_changeset" |> String.to_atom
if Code.ensure_loaded?(module) && function_exported(module, changeset_function_name, 1) do
apply(module, changset_function_name, record) |> repo.insert!
else
repo.insert! record
end
end
end
Is there a way to handle creation of has_one
and has_many
associations? Namely, to have something like:
def factory(:article) do
%Article{
title: "My Awesome Article",
comments: %Comment{
body: "I will be always here!"
}
}
end
and to create it
article_with_comment = create(:article)
Current, if I try to do this I get error ... Assocs can only be manipulated via changesets, be it on insert, update or delete.
when Repo.insert!
is called.
If instead model to insert is converted to changeset first, associations are inserted as well,
%{__struct__: model} = record
struct(model) |> Ecto.Changeset.change(drop_ecto_fields(record)) |> repo.insert!
Not sure if this is the right approach though...
Say I want to get the project set up locally, how would I go about doing that?
(I assume it's something simple like mix deps.get && mix test
)
Seems like something worth documenting so there is a low barrier to contribute ๐
fields_for (now params_for) doesn't populate the IDs of relations when you build them.
Imagine you're modeling a play in a sport...
schema "plays" do
belongs_to :game, Namespace.Game
belongs_to :possession_team, Namespace.Team
...
end
With
def factory(:play) do
%Namespace.Play{
game: create(:game),
possession_team: create(:team),
}
end
When you do fields_for(:play)
game_id and possession_team_id are nil. It makes it harder to use for sending data to an API, for example. It would be nice if those could be populated.
Technically when you do build(:play)
the same is true. It seems like Ecto doesn't populate those IDs until after it's saved?
These would just delegate to the create
versions. I think this maps better to the Ecto terminology of inserting records.
insert(:user)
insert_pair(:user)
insert_list(:user)
Thoughts?
Using polymorphic associations and passing the assoc_id
to build/2
allows me to build records, but create
fails as its trying to build with the abstract table name rather than the concrete one. The fun thing is, I can manually pipe whatever was built into a Repo.insert!
and my insertion is successful.
# factory
def factory(:vendor_image) do
name = sequence(:filename, &"file-#{&1}.jpg")
build_assoc(%Vendor{}, :images, [
filename: name,
alt: "Caption text for #{name}",
medium: "priv/images/medium-#{name}",
original: "priv/images/original-#{name}"
])
end
# usage..
v = Factory.create(:vendor)
# fails, because its attempting to insert into the abstract table
i = Factory.create(:vendor_image, assoc_id: v.id)
# succeeds
i = Factory.build(:vendor_image, assoc_id: v.id) |> Repo.insert!
I needed to generate an id for use with mandrill. I did this
def factory(:mandrill_id) do
sequence(:mandrill_id, &"mandrill_message_id_#{&1}")
end
The problem is that build attempts to merge a map so you have to do factory(:mandrill_id)
. I think it would be better if you could use build
so it matches with the other factories
EDIT: This might be a bad idea. Might be better to just create a function called mandrill_id
that uses the sequence. Problem solved. Though maybe there's another use case for wanting to build something other than a map or keyword list?
See #54 for why this would be nice.
I think it would be good to add to_date_time
, to_date
, to_time
and corresponding sigil_t
sigil_d
and sigil_(not_sure_yet)
sigils that can be imported and would convert a regular Erlang timestamp to the appropriate Ecto type. I imagine you could do something like this to make it easy to use:
defmodule MyApp.Factory do
# This would import the functions for use inside the factory
use ExMachina.Ecto, repo: MyRepo
# I think this is nice because importing the sigils is optional so there are no accidental collisions
defmacro __using__(_opts) do
quote do
import MyApp.Factory
# Imports the sigils so you can them in your tests
import ExMachina.TimeHelpers
end
end
end
# In a test
use MyApp.Factory # import the factory and the time helpers
create(:article, inserted_at: ~t(GoodTimes.a_day_ago)) # GoodTimes generates erlang time stamps
I just got done with a hangout with a few other Elixir/Phoenix users. One thing that was confusing to them was what the attrs
are for. Since the example has them being used for assocs, it seems like that's all you could use them for. It is also unclear at what point the attrs are passed in, and what they are.
I think we should explain that the attrs
are what you pass in as the second option to the build/create functions. Also maybe an example that uses attrs to build an email address from first and last name or something?
I accidentally did this:
appointment_booking = create(:appointment_booking)
|> set_appointment_price(500)
|> insert
And got this error:
* (Ecto.ConstraintError) constraint error when attempting to insert model:
* unique: appointment_booking_pkey
If you would like to convert this constraint into an error, please
call unique_constraint/3 in your changeset and define the proper
constraint name. The changeset has not defined any constraint.
I think it would be helpful if it said something like:
you called `insert` on #{the record} but it was already saved. You can only call insert on records that haven't been inserted yet
Or something along those lines. This is pretty low priority, but might be nice to have
Do this for 1.0
Hi,
I tried to work by the instructions. If I am correct, the readme refers to the new way of declaring factory methods, and the current version specified by the readme - 0.6.1 - does not yet include that code.
Readme:
def user_factory do
%User{
name: "Jane Smith",
email: sequence(:email, &"email-#{&1}@example.com"),
}
end
build(:comment, attrs)
Current release code in 0.6.1 ex_machina.ex:
def build(module, factory_name, attrs \\ %{}) do
attrs = Enum.into(attrs, %{})
module.factory(factory_name) |> Map.merge(attrs)
end
New code in ex_machina.ex:
https://github.com/thoughtbot/ex_machina/blob/master/lib/ex_machina.ex#L98
def build(module, factory_name, attrs \\ %{}) do
attrs = Enum.into(attrs, %{})
function_name = Atom.to_string(factory_name) <> "_factory" |> String.to_atom
if Code.ensure_loaded?(module) && function_exported?(module, function_name, 0) do
apply(module, function_name, []) |> do_merge(attrs)
else
raise UndefinedFactoryError, factory_name
end
end
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.