Giter VIP home page Giter VIP logo

bbmustache's Introduction

bbmustache

CircleCI hex.pm version

Binary pattern match Based Mustache template engine for Erlang/OTP.

Overview

  • Binary pattern match based mustache template engine for Erlang/OTP.
    • It means do not use regular expressions.
  • Support maps and associative arrays.
  • Officially support is OTP24 or later.

What is Mustache ?

A logic-less templates.

Usage

Quick start

$ git clone https://github.com/soranoba/bbmustache.git
$ cd bbmustache
$ make start
Erlang/OTP 17 [erts-6.3] [source-f9282c6] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:true]

Eshell V6.3  (abort with ^G)
1> bbmustache:render(<<"{{name}}">>, #{"name" => "hoge"}).
<<"hoge">>
2> bbmustache:render(<<"{{name}}">>, [{"name", "hoge"}]).
<<"hoge">>

Use as a library

Add the following settings.

%% rebar (rebar.config)

{deps,
  [
   {bbmustache, ".*", {git, "https://github.com/soranoba/bbmustache.git", {branch, "master"}}}
  ]}.

%% rebar3 (rebar.config)

{deps, [bbmustache]}.

How to use simple Mustache

Map

1> bbmustache:render(<<"{{name}}">>, #{"name" => "hoge"}).
<<"hoge">>

2> Template1 = bbmustache:parse_binary(<<"{{name}}">>).
...
3> bbmustache:compile(Template1, #{"name" => "hoge"}).
<<"hoge">>

4> Template2 = bbmustache:parse_file(<<"./hoge.mustache">>).
...
5> bbmustache:compile(Template2, #{"name" => "hoge"}).
<<"hoge">>

Associative array

1> bbmustache:render(<<"{{name}}">>, [{"name", "hoge"}]).
<<"hoge">>

2> Template1 = bbmustache:parse_binary(<<"{{name}}">>).
...
3> bbmustache:compile(Template1, [{"name", "hoge"}]).
<<"hoge">>

4> Template2 = bbmustache:parse_file(<<"./hoge.mustache">>).
...
5> bbmustache:compile(Template2, [{"name", "hoge"}]).
<<"hoge">>

Use as a command-line tool

make escriptize
echo '{"name", "hoge"}.' > vars.config
echo '{{name}}' > template.mustache
./bbmustache -d vars.config template.mustache
hoge

Data files (-d) support a single assoc list, a single map, and consult format.
Note: the behind term has a high priority in all cases. it is a result of supporting to allow for embedding relative file paths as in config.

More information

  • For the alias of mustache, Please refer to ManPage and Specification
  • For the options of this library, please see doc
  • For the functions supported by this library, please see here

FAQ

Avoid http escaping

%% Please use `{{{tag}}}`
1> bbmustache:render(<<"<h1>{{{title}}}</h1>">>, #{"title" => "I like Erlang & mustache"}).
<<"<h1>I like Erlang & mustache</h1>">>

%% If you should not want to use `{{{tag}}}`, escape_fun can be use.
1> bbmustache:render(<<"<h1>{{title}}</h1>">>, #{"title" => "I like Erlang & mustache"}, [{escape_fun, fun(X) -> X end}]).
<<"<h1>I like Erlang & mustache</h1>">>

Already used { and } for other uses (like escript)

1> io:format(bbmustache:render(<<"
1> {{=<< >>=}}
1> {deps, [
1>   <<#deps>>
1>   {<<name>>, \"<<version>>\"}<<^last?>>,<</last?>>
1>   <</deps>>
1> ]}.
1> ">>, #{"deps" => [
1>   #{"name" => "bbmustache", "version" => "1.6.0"},
1>   #{"name" => "jsone", "version" => "1.4.6", "last?" => true}
1> ]})).

{deps, [
  {bbmustache, "1.6.0"},
  {jsone, "1.4.6"}
]}.
ok

Want to use something other than string for key

1> bbmustache:render(<<"<h1>{{{title}}}</h1>">>, #{title => "I like Erlang & mustache"}, [{key_type, atom}]).
<<"<h1>I like Erlang & mustache</h1>">>

2> bbmustache:render(<<"<h1>{{{title}}}</h1>">>, #{<<"title">> => "I like Erlang & mustache"}, [{key_type, binary}]).
<<"<h1>I like Erlang & mustache</h1>">>

Want to provide a custom serializer for Erlang Terms

1> bbmustache:render(<<"<h1>{{title}}</h1>">>, #{title => "I like Erlang & mustache"}, [{key_type, atom}, {value_serializer, fun(X) -> X end}]).
<<"<h1>I like Erlang &amp; mustache</h1>">>

2> bbmustache:render(<<"<h1>{{{title}}}</h1>">>, #{<<"title">> => "I like Erlang & mustache"}, [{key_type, binary}, {value_serializer, fun(X) -> <<"replaced">> end}]).
<<"<h1>replaced</h1>">>

3> bbmustache:render(<<"<h1>{{{title}}}</h1>">>, #{<<"title">> => #{<<"nested">> => <<"value">>}}, [{key_type, binary}, {value_serializer, fun(X) -> jsone:encode(X) end}]).
<<"<h1>{\"nested\": \"value\"}</h1>">>

4> bbmustache:render(<<"<h1>{{title}}</h1>">>, #{<<"title">> => #{<<"nested">> => <<"value">>}}, [{key_type, binary}, {value_serializer, fun(X) -> jsone:encode(X) end}]).
<<"<h1>{&quot;nested&quot;:&quot;value&quot;}</h1>">>

Attention

  • Lambda expression is included wasted processing.
    • Because it is optimized to parse_binary/1 + compile/2.

Comparison with other libraries

Benchmarks and check the reference implementation

Contribute

Pull request is welcome =D

License

MIT License

bbmustache's People

Contributors

bjyoungblood avatar codeadict avatar kianmeng avatar soranoba avatar tsloughter 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

bbmustache's Issues

Escaping overlay vars (mustache variables) inside template files

Hi

How to use bbmustache to allow to scape mustache variables, what I want is:

  • I have a file (my_template_sys.config.tpl) that has overlay vars, for example, we always use a config like: {environment, {{environment_var}}}.
  • When I run rebar3 new my_template myservice the environment config in sys.config is generated {environment, } because in the rebar3 template generation the variable is expanded and because does not exists in the context of the rebar3 template it will be replaced by โ€œnothingโ€

Any sugestion?

Thank you

Pedro

Rendering a template always accesses file on disk

When rendering templates from files, I noticed that the template file is accessed each time the template is compiled. For most scenarios this is probably not mission critical but still unneccessary IO.

I used erlydtl in previous projects and remembered that they compile the templates into their own modules which export a render function.

my_template:render([variables])

I liked the light weight nature of your library and so I wrote a lightweight template loader that creates modules for each template when the application is loaded.

In its current form it is probably not 100% suitable to integrate into your library and maybe you explicitly don't want such functionality.

If you are curious however, this is how I implemented it:
https://github.com/hukl/shortcut.io_new/blob/master/src/scio_view.erl

For each .mustache file in my views folder, a corresponding module with a render function is generated in which you pass in your content.

In my example I omitted the possibility to pass along render options or escape functions, but that could be easily added to make it more suitable for generic use in your library.

Crash under certain conditions

bbmustache v1.4.0

1> bbmustache:render(<<"{{#parent}}{{parent.child}}{{/parent}}">>, #{"parent" => true}).
** exception error: no function clause matching proplists:lookup("child",true) (proplists.erl, line 133)
     in function  bbmustache:find_data/2 (/xxxxxx/bbmustache/_build/dev/lib/bbmustache/src/bbmustache.erl, line 577)
     in call from bbmustache:get_data_recursive/4 (/xxxxxxx/bbmustache/_build/dev/lib/bbmustache/src/bbmustache.erl, line 562) 
     in call from bbmustache:compile_impl/4 (/xxxxxxxx/bbmustache/_build/dev/lib/bbmustache/src/bbmustache.erl, line 187)
     in call from bbmustache:compile_impl/4 (/xxxxxxxx/bbmustache/_build/dev/lib/bbmustache/src/bbmustache.erl, line 205)
     in call from bbmustache:compile/3 (/xxxxxxx/bbmustache/_build/dev/lib/bbmustache/src/bbmustache.erl, line 172)

A crash occurs when parent is specified term other than bbmustache:data ().

Use a fun as Data

Currently one can supply a proplist or a map as Data argument to render function. However it may be useful to use a custom function as well. This should be very easy to implement: just add a when is_function clause to find_data/2...

I can create a PR unless anyone got objections.

Wrong Version in .app file

The package in hex.pm doesn't work properly with rebar3 because the version in the .app.src is set to git. This means rebar3 will always think it has the wrong version of bbmustache and continually try to upgrade when building a project.

If you use rebar3 hex cut you can easily set the new version in .app.src and publish https://asciinema.org/a/32q4wprb5crr3zpvldlv4f4bs

Problems loading partials on Elixir project

Hey,

I'm working on an elixir project and I'm having some problems to make partials to work. I've the partials in a local file, same place as the templates.

/templates/my_template.mustache

{{> partial}}

/templates/partial.mustache

<h1>Hey there</h1>

I keep getting an empty string when I render the my_template. Should it work out of the box like this or am I missing something ๐Ÿ˜… ? Is there any examples I could look into?

Thanks!

Backward compatibility

Hi!

I've noticed that the newest version v1.1.0 is not backward compatible with the version v1.0.4 in the escaping context.
v1.04:

bbmustache:render(<<"{{{profile_dir}}}/bin/app1_402184">>, [{profile_dir, "/home/test/test"}], [{key_type, atom}]).
<<"/home/test/test/bin/app1_402184">>

v1.1.0:

bbmustache:render(<<"{{profile_dir}}/bin/app1_402184">>, [{profile_dir, "/home/test/test"}], [{key_type, atom}]).
<<"&#x2F;home&#x2F;test&#x2F;test/bin/app1_402184">>

This example is taken from rebar3 common tests. I tried to upgrade bbmustache for rebar3, but due to this incompatibility I could not. Event, when I fixed the test, there is a problem with rebar3 itself. I think in many places it uses bbmustache:render function with "paths" as a variables, which are escaped in v.1.1.0.
It would be nice if new version would be backaward compatible or maybe as a workaround , the option unescape could be passed to the render function. It would be much easier only to add this parameter. What do you think about that?

Allow array access for lists

Hey,

Currently i have need to use the mustache array index functionality.

Currently if i run:

1> bbmustache:render(<<"check out my template: {{a.1}}">>, #{"a" => ["foo", "bar"]})
<<"check out my template: ">>

From what i understand from mustache we should see:

1> bbmustache:render(<<"check out my template: {{a.1}}">>, #{"a" => ["foo", "bar"]})
<<"check out my template: foo">>

From looking around online both .1. and .[1]. seem to be a supported syntax in most libraries, though the former seems more common to me.

I've got a PR that I can put up with the fix for this if you it's decided to be a good addition to the library.

Variable test not matching spec

Running the variable test from the spec results in something I didn't expect:

1> bbmustache:render(<<"\"{{#foo}}{{.}} is {{foo}}{{/foo}}\"">>, #{"foo" => "bar"}).
<<"\"98 is bar97 is bar114 is bar\"">>

I was expecting the result <<"\"bar is bar\"">>. Perhaps I'm missing something or is this a bug?

Bug? intra-tag whitespace not rendering

(in Elixir...)

A tag with a space in the middle doesn't get rendered. Is this intentional, or a bug?

iex(12)> :bbmustache.render("{{foo bar}}", [{'foo bar', "potato"}]) 
""

I can't find any thing in the Mustache docs about white space being disallowed.

How to render lists?

What am I doing wrong here?

{{#events}}
Hi {{act}}!
{{/events}}
Events = [{events, [{act, "Pink Floyd"}] }],
Parsed = bbmustache:parse_file(TemplateFileName),
bbmustache:compile(Parsed, Events).

The bbmustache.compile/2 returns <<"">> and I expected <<"Hi Pink Floyd!">>

Thanks!

(Edit: added actual return value and what I expected.)

Incompatible typespecs

In the following code:

%% @equiv compile(parse_binary(Bin), Data, Options)
-spec render(binary(), data(), [render_option()]) -> binary().
render(Bin, Data, Options) ->
compile(parse_binary(Bin, Options), Data, Options).
%% @equiv parse_binary(Bin, [])
-spec parse_binary(binary()) -> template().
parse_binary(Bin) when is_binary(Bin) ->
parse_binary(Bin, []).
%% @doc Create a {@link template/0} from a binary.
-spec parse_binary(binary(), [parse_option()]) -> template().
parse_binary(Bin, Options) ->
{State, Data} = parse(#state{}, Bin),
parse_remaining_partials(State, #?MODULE{data = Data}, Options).

it appears that render/3 accepts [render_option()] whereas parse_binary/2 accepts [parse_option()]. Unfortunately, the same data is used in both cases (Options variable), and the types clash as defined:

-type parse_option() :: raise_on_partial_miss.
%% - raise_on_partial_miss: If the template used in partials does not found, it will throw an exception (error).
-type compile_option() :: {key_type, atom | binary | string}
| raise_on_context_miss
| {escape_fun, fun((binary()) -> binary())}.
%% - key_type: Specify the type of the key in {@link data/0}. Default value is `string'.
%% - raise_on_context_miss: If key exists in template does not exist in data, it will throw an exception (error).
%% - escape_fun: Specify your own escape function.
-type render_option() :: compile_option() | parse_option().

In that case, the only valid options for these functions are [] and [raise_on_partial_miss] (essentially parse_option() since parse_binary/2 forces a strict subset of acceptable types.

Either the parse_binary/2 function should see its typespec corrected to accept more types, or render/3 should see its typespec narrowed down to be more restrictive.

If both type specifications are correct, then there should be a function like this:

-spec split_options([render_option()]) -> {[render_option()], [parse_option()]}.
split_options(Options) ->
    Parse = case lists:member(raise_on_partial_miss, Options) of
        true -> [raise_on_partial_miss];
        false -> []
    end,
    {Options, Parse}.

defined that can be called in:

-spec render(binary(), data(), [render_option()]) -> binary().
render(Bin, Data, Options) ->
     {CompileOpts, ParseOpts} = split_options(Options),
     compile(parse_binary(Bin, ParseOptions), Data, CompileOptions)

which I think should satisfy dialyzer without changing program semantics. You could also use something like extract_parse_options([render_option()]) -> [parse_option()] and reuse Options for the compile/3 call, which would be fine as well.

Make escaping optional

I think it would be useful to not do html escaping for non web page rendering purposes.

Wrong parsing when closing tag is on separate line

When rendering template I get unexpected result:

iex(7)> :bbmustache.render "{{#foo}}bar\n{{/foo}}    \n", [{'foo', fn x, _ -> "<#{x}>" end}]
"<bar\n{{/fo>"

Function receives argument with extra chars.
Must be <bar\n>, got <bar\n{{/fo>.
It seems to be a wrong parsing when closing tag is on a separate line:

iex(8)> :bbmustache.parse_binary "{{#foo}}bar\n{{/foo}}    \n"
{:bbmustache, [{:"#", ["foo"], ["bar\n"], "bar\n{{/fo"}], [], [], [], []}

Render plain lists

I can't find it in the mustache documentation, but using the demo ( http://mustache.github.io/#demo ) shows that this:

{{#mylist}}
  {{.}}
{{/mylist}}

Will work on lists that look like this:

mylist: ["Item 1", "Item 2", "Item 3"]

Producing output like this:

Item 1
Item 2
Item 3

I was hoping that an erlang map that looked like this:

#{ "mylist" => ["Item 1", "Item 2", "Item 3"] }

would allow the same behavior, but it seems to be unsupported.

Keys as atoms

Rebar and relx templates use atoms as keys. Could mustache convert the data key to an atom if the value isn't found for the string, at least?

Wrong filename for missing include errors

When including a non-existing file with {{> my_file_name}} the error generated contains the file name of the original template, not the included file: {context_missing, {file_not_found, <<"the_template">>}}. The error should be {context_missing, {file_not_found, <<"my_file_name">>}}.

Reference implementation (feature v1.2.0)

Overview

Additional functions

โœ… Support the dotted names. (child's variables)

data:
    {"parent.child" : "before", "parent": {"child" : "after"}}
template:
    {{parent.child}}
v1.1.x result:
    before
v1.2.x result:
    after

๐Ÿ’ก Dot in the tag become the meaning as a separator.

โœ… Support the recursive partials.

๐Ÿ’ก It had become an infinite loop in v1.1.x.

โœ… Support the indent in partials.

data: 
    {"sections" : [ {"section" : "1st section"}, {"section" : "2nd section"}]}
template:
    {{>a}}
a.mustache
    {{# sections }}
    {{ section }}
        {{>b}}
    {{/ sections }}
b.mustache
    {{ section }}
        {{>c}}
c.mustache
    Hello, indent paritals !
    2nd line
    3rd line
result:
    1st section
        1st section
            Hello, indent paritals !
            2nd line
            3rd line
    2nd section
        2nd section
            Hello, indent paritals !
            2nd line
            3rd line

Modified function

โœ… Change the escape characters. #13

๐Ÿ’ก Escape characters is & " > <

โœ… Reset the delimiters in the partial sections.

๐Ÿ’ก It did not reset the delimiters in v1.1.x.

โœ… If the tag including the space has been specified, it became to delete the space.

data: 
     {"keep space" : "keep_space_tag", "deletespace" : "delete_space_tag"}
template:
     {{ keep space }}{{ delete space }}
v1.1.x result:
     keep_space_tag
v1.2.x result:
     delete_space_tag

๐Ÿ’ก Tag MUST NOT include the spaces. The current behavior is likely to change in the future.

โœ… Change of the processing of the new line.

Except for the Variables (that is, {{key}} or {{{key}}}, {{&key}}),
this line is not included in the output, when there is only a tag on the line.

Minor update (with backward compatibility)

  • Support the proplist that include empty tuple : [{}]
  • Change of internal state.
  • update the doc.

NOTE

This time, those that saw off the implementation.

Adding custom serialization functions to bbmustache.

Hi,

Our team currently has a need to offer users the ability to output maps as types for mustache serialization. I was wondering if I could add custom serializers to the library so that the user could select specific serialization functions for types not supported currently?

Current Behaviour

:bbmustache.render("{{test}}", %{"test" => %{"weee" => 2}}, key_type: :binary)
** (ArgumentError) argument error
    :erlang.iolist_to_binary(%{"weee" => 2})
    (bbmustache) /Users/ethan/new_faspex4/f4-dev-setup/microservices/workflow-engine/deps/bbmustache/src/bbmustache.erl:233: :bbmustache.compile_impl/4
    (bbmustache) /Users/ethan/new_faspex4/f4-dev-setup/microservices/workflow-engine/deps/bbmustache/src/bbmustache.erl:218: :bbmustache.compile/3

Proposed Behaviour

iex(1)> :bbmustache.render("{{test}}", %{"test" => %{"weee" => 2}}, key_type: :binary, map_serializer: fn x -> JSON.encode!() end)
"{\"weee\":2}"

Unicode support in value fetching.

See erlang/rebar3#2644

The issue is located within https://github.com/soranoba/bbmustache, specifically at

Value = iolist_to_binary(ValueSerializer(get_data_recursive(Keys, Data, <<>>, State))),
, where the code should likely call for unicode:characters_to_binary instead of iolist_to_binary.

There are likely other locations for such calls, but sending in unicode data that has a wide representation can break when the codepoints get too large with the current calls.

Allow disabling of html escaping

This is a rehash of #10, as i'm sure you're aware, bbmustache is being used in relx to template scripts and overlay vars, in this context we don't really need html escaping and it would be useful to disable it completely, this way users wouldn't have to use the {{{ syntax to escape binary values in overlays variables for example.

Tag lookup doesn't descend the context stack

Given the following map:

Map = #{
   "author" => "Bill",
   "os" => [
      #{ "name" => "Ubuntu", "version" => "12" },
      #{ "name" => "Ubuntu", "version" => "16" }
   ] }.

And the following template:

Tmpl = <<"Author: {{author}}, OSes: {{#os}}{{name}}/{{version}} ({{author}}) {{/os}}">>.

The following result is in error:

1> bbmustache:render(Tmpl, Map).
<<"Author: Bill, OSes: Ubuntu/12 () Ubuntu/16 () ">>

The output should be <<"Author: Bill, OSes: Ubuntu/12 (Bill) Ubuntu/16 (Bill) ">>.

Run bbmustache from command line

I need to instantiate templates in files using erlang terms (also in other files).

I can open a PR with Main func on bbmustache and then we rebar3 escriptalise it (same as we do for other erlang tools like elvis or rebar3).

Does it make sense ?
Thanks

Feature request: Allow getting partials from other sources

In our Elixir project, we couldn't save partials in the filesystem. To work around this, we wrapped bbmustache like so:

defmodule OurApp.Mustache do
  @partial_templates %{
    foo: "Foo",
    bar: "Bar"
  }

  def render(bin, data) do
    {:bbmustache, tags, partial_keys, options, indents, context_stack} =
      :bbmustache.parse_binary(bin)

    :bbmustache.compile(
      {
        :bbmustache,
        tags,
        parse_remaining_partials(partial_keys, [], []),
        options,
        indents,
        context_stack
      },
      data,
      key_type: :atom,
      raise_on_context_miss: true
    )
  end

  defp parse_remaining_partials([] = _keys, partials, _parsed_keys), do: partials

  defp parse_remaining_partials([key | rest_of_keys], partials, parsed_keys) do
    if key in parsed_keys do
      parse_remaining_partials(rest_of_keys, partials, parsed_keys)
    else
      bin = Map.fetch!(@partial_templates, String.to_existing_atom(key))

      {:bbmustache, tags, nested_keys, _options, _indents, _context_stack} =
        :bbmustache.parse_binary(bin)

      parse_remaining_partials(rest_of_keys ++ nested_keys, [{key, tags} | partials], [
        key | parsed_keys
      ])
    end
  end
end

However, dialyzer is complaining about the opacity of :bbmustache.template() being broken because it is matched against a pattern.

It would be nice if :bbmustache.template() weren't opaque. Better yet, it would be nice if :bbmustache.parse_option() allows get_partial_fun which defaults to the current implementation of reading the partial from a file.

I'd love to submit a PR but my Erlang skills are severely lacking.

Anyway to skip over unclosed tags?

Scenario

I have a LiveView text area that supports mustache templates to be entered on each keystroke, I call :bbmustache.render(text, %{"time" => DateTime.utc_now()}, key_type: :binary). As the user is typing their input like this It is currently { then they hit one more { (It currently {{) the bbmustache will blow up with an unclosed_tags error.

Is there a way to have bbmustache just ignore unclosed tags and render them as {{ until a closed tag is detected?

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.