stimulusreflex / futurism Goto Github PK
View Code? Open in Web Editor NEWLazy-load Rails partials via CableReady
License: MIT License
Lazy-load Rails partials via CableReady
License: MIT License
Please help us help you by filling out any applicable information in this template and removing the rest!
Thank you for implementing the controller override @rickychilcott , @julianrubisch .
Unfortunately, I was not available to test this while it was in PR against my codebase, however I'm doing this now and of course I seem to have an issue
Steps to reproduce the behavior
A clear and concise description of what you expected to happen.
/Users/scott/Code/Ollie/OllieOrder/config/initializers/futurism.rb:3 :
1: require 'pry'
2: binding.pry
=> 3: Futurism.default_controller = OrganizationsController
[7] pry(main)> Futurism.default_controller = OrganizationsController
NameError: Couldn't find PdfHelper, expected it to be defined in helpers/pdf_helper.rb
from /Users/scott/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/actionpack-6.0.3.4/lib/abstract_controller/helpers.rb:163:in `rescue in block in modules_for_helpers'
Caused by LoadError: Unable to autoload constant PdfHelper, expected /Users/scott/Code/Ollie/OllieOrder/app/helpers/pdf_helper.rb to define it
from /Users/scott/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/activesupport-6.0.3.4/lib/active_support/dependencies.rb:550:in `load_missing_constant'
[8] pry(main)>
The pdf_helper seems to be a red herring, but do is there a reason I cannot use the controller constants in the initializer?
There are two ways in which this could be tackled:
the „canonical“ way would be via PWA/Service worker. The problem is that only one SW per domain can exist, so people would have to integrate this with their own implementations. This would boil down to a (hard and ungrateful) documentation task.
We could also go and roll our own implementation, as for example with the Turbolinks cache store and sessionStorage. The problem is that it only holds a few MB and isn’t expireable: https://stackoverflow.com/questions/15171711/expiry-of-sessionstorage
this could also be a case for IndexedDB/localForage, but still we would have to code the expiration logic ourselves: https://stackoverflow.com/questions/35511033/how-to-apply-expiry-times-to-keys-in-html5-indexeddb/35518068
That said, we could provide a unified interface for both Turbolinks cache storage and fragment caching via localForage
While developing some examples, I've passed in some bad parameters to a partial or there have been issues in my partials that causes futurism to never resolve the partial. As such, cable ready never sends back a valid partial.
While in development mode, I'd like to see an error that replaces my placeholder ideally with a stacktrace or at least something that tells me an error has occurred and to check the logs. The logs could indicate the an error has occured.
Right now futurism relies on a ActionController::Renderer
with a complex logic to resolve the correct controller for a given URL etc.
Turns out this might not be necessary, we could just use ActionView::PartialRenderer
if we're able to obtain the correct view_context
, which would drastically simplify things (and perhaps also speed them up a bit)
I'm trying to add Futurism to a legacy Rails app running Rails 5.2, and I'm running into problems. Trying to recreate in a new 5.2 app has the same issues.
Running bin/rails futurism:install --trace
gives this error:
** Invoke futurism:install (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute futurism:install
yarn add v1.22.17
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved 1 new dependency.
info Direct dependencies
└─ @stimulus_reflex/[email protected]
info All dependencies
└─ @stimulus_reflex/[email protected]
Done in 2.67s.
Updating
rails aborted!
TypeError: no implicit conversion of nil into String
/home/user/.rbenv/versions/2.6.9/lib/ruby/gems/2.6.0/gems/futurism-1.1.0/lib/tasks/futurism_tasks.rake:19:in `initialize'
/home/user/.rbenv/versions/2.6.9/lib/ruby/gems/2.6.0/gems/futurism-1.1.0/lib/tasks/futurism_tasks.rake:19:in `open'
/home/user/.rbenv/versions/2.6.9/lib/ruby/gems/2.6.0/gems/futurism-1.1.0/lib/tasks/futurism_tasks.rake:19:in `block (2 levels) in <main>'
/home/user/.rbenv/versions/2.6.9/lib/ruby/gems/2.6.0/gems/rake-13.0.6/lib/rake/task.rb:281:in `block in execute'
/home/user/.rbenv/versions/2.6.9/lib/ruby/gems/2.6.0/gems/rake-13.0.6/lib/rake/task.rb:281:in `each'
/home/user/.rbenv/versions/2.6.9/lib/ruby/gems/2.6.0/gems/rake-13.0.6/lib/rake/task.rb:281:in `execute'
/home/user/.rbenv/versions/2.6.9/lib/ruby/gems/2.6.0/gems/rake-13.0.6/lib/rake/task.rb:219:in `block in invoke_with_call_chain'
/home/user/.rbenv/versions/2.6.9/lib/ruby/2.6.0/monitor.rb:235:in `mon_synchronize'
/home/user/.rbenv/versions/2.6.9/lib/ruby/gems/2.6.0/gems/rake-13.0.6/lib/rake/task.rb:199:in `invoke_with_call_chain'
/home/user/.rbenv/versions/2.6.9/lib/ruby/gems/2.6.0/gems/rake-13.0.6/lib/rake/task.rb:188:in `invoke'
/home/user/.rbenv/versions/2.6.9/lib/ruby/gems/2.6.0/gems/rake-13.0.6/lib/rake/application.rb:160:in `invoke_task'
/home/user/.rbenv/versions/2.6.9/lib/ruby/gems/2.6.0/gems/rake-13.0.6/lib/rake/application.rb:116:in `block (2 levels) in top_level'
/home/user/.rbenv/versions/2.6.9/lib/ruby/gems/2.6.0/gems/rake-13.0.6/lib/rake/application.rb:116:in `each'
/home/user/.rbenv/versions/2.6.9/lib/ruby/gems/2.6.0/gems/rake-13.0.6/lib/rake/application.rb:116:in `block in top_level'
/home/user/.rbenv/versions/2.6.9/lib/ruby/gems/2.6.0/gems/rake-13.0.6/lib/rake/application.rb:125:in `run_with_threads'
/home/user/.rbenv/versions/2.6.9/lib/ruby/gems/2.6.0/gems/rake-13.0.6/lib/rake/application.rb:110:in `top_level'
/home/user/.rbenv/versions/2.6.9/lib/ruby/gems/2.6.0/gems/railties-5.2.6.2/lib/rails/commands/rake/rake_command.rb:23:in `block in perform'
/home/user/.rbenv/versions/2.6.9/lib/ruby/gems/2.6.0/gems/rake-13.0.6/lib/rake/application.rb:186:in `standard_exception_handling'
/home/user/.rbenv/versions/2.6.9/lib/ruby/gems/2.6.0/gems/railties-5.2.6.2/lib/rails/commands/rake/rake_command.rb:20:in `perform'
/home/user/.rbenv/versions/2.6.9/lib/ruby/gems/2.6.0/gems/railties-5.2.6.2/lib/rails/command.rb:48:in `invoke'
/home/user/.rbenv/versions/2.6.9/lib/ruby/gems/2.6.0/gems/railties-5.2.6.2/lib/rails/commands.rb:18:in `<main>'
/home/user/.rbenv/versions/2.6.9/lib/ruby/gems/2.6.0/gems/bootsnap-1.10.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
/home/user/.rbenv/versions/2.6.9/lib/ruby/gems/2.6.0/gems/bootsnap-1.10.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
bin/rails:4:in `<main>'
Tasks: TOP => futurism:install
Create a new Rails 5.2 install with actioncable, then bundle add futurism and bin/rails futurism:install
I expected it to install, or to give an error message with more information on what went wrong.
[see above]
[not applicable]
Allow to pass locals when rendering a collection
In order to stay as close as possible to the render
method in Rails it would make sense to support the passing of locals when rendering a collection.
While you can do this with render
:
<%= render partial: 'items/card', collection: @cards, as: :card, locals: { selected: true } %>
You can't do this with futurize
:
<%= futurize partial: 'items/card', collection: @cards, as: :card, extends: :div, locals: { selected: true } do %>
<div class="spinner"></div>
<% end %>
To most logical would be (as described above):
<%= futurize partial: 'items/card', collection: @cards, as: :card, extends: :div, locals: { selected: true } do %>
<div class="spinner"></div>
<% end %>
I just tried to implement my first "futurism" tryout. Although all components seem to connect, I get an error when the element tries to update: (I reduced the data-signed-params string in below's printout - as it is actually 117K bytes long...)
Futurism::Channel#receive({"signed_params"=>["BAh7BzoMcGFydGlhbEkiIXdpbmtlbC9zdG9yZV9vcmRlcl...f2b7fbf72d7c5a5df5b15"], "sgids"=>[nil]}) Rendered winkel/store_order_lines/_sol.html.haml (Duration: 0.2ms | Allocations: 53) [ActionCable] Broadcasting to Futurism::Channel: {"cableReady"=>true, "operations"=>{"outerHtml"=>[{"selector"=>"[data-signed-params='BAh7BzoMcGFydGlhbEkiIXdpbmtlbC9zdG9yZV9vcmRlcl...f2b7fbf72d7c5a5df5b15']", "html"=>"<p>\n31484\n</p>\n"}]}} Could not execute command from ({"command"=>"message", "identifier"=>"{\"channel\":\"Futurism::Channel\"}", "data"=>"{\"signed_params\":[\"BAh7BzoMcGFydGlhbEkiIXdpbmtlbC9zdG9yZV9vcmRlcl...f2b7fbf72d7c5a5df5b15\"],\"sgids\":[null]}"}) [PG::InvalidParameterValue - ERROR: payload string too long ]: /home/danny/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/actioncable-6.0.3.2/lib/action_cable/subscription_adapter/postgresql.rb:20:in
exec' | /home/danny/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/actioncable-6.0.3.2/lib/action_cable/subscription_adapter/postgresql.rb:20:in block in broadcast' | /home/danny/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/actioncable-6.0.3.2/lib/action_cable/subscription_adapter/postgresql.rb:54:in
block in with_broadcast_connection' | /home/danny/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/connection_pool.rb:471:in with_connection' | /home/danny/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/actioncable-6.0.3.2/lib/action_cable/subscription_adapter/postgresql.rb:51:in
with_broadcast_connection'`
Implementation itself is very simple:
- sols.each do |sol| = futurize partial: "winkel/store_order_lines/sol", locals: {sol: sol}, extends: :div, html_options: {style: "width: 50px; height: 50px; display: block; "} do %div.spinner
and for winkel/store_order_lines/_sol.html.haml:
%p = sol.id
Any idea what I'm doing wrong - or why the signed params string is so long?
Futurism version: 0.4.1
in addition to turbolinks
Please help us help you by filling out any applicable information in this template and removing the rest!
A clear and concise description of what the bug is.
Add any link_to
to a rendered partial
A clear and concise description of what you expected to happen.
renderer = ApplicationController.renderer.new(
http_host: request.host_with_port,
https: (request.protocol == 'https://')
)
html = renderer.render(
partial: 'campaign_events/campaign_event',
locals: { campaign_event: new_shout },
layout: false
)
Image src is prefixed with http://example.org
instead of http://localhost:3000
.
I have an ActiveRecord model named Element
in this project, and instantiate a Element.new
in a view file (for authorization purposes). Well... that conflicts with Futurism::Helpers::Element
. Since we just include the Futurism::Helpers
module, it gets added into the namespace in views and controllers.
This is workaroundable on my end by referencing ::Element.new
, but it was quite confusing.
Can we consider renaming the Element
class to FuturismElement
?
Not a problem as such, just a desire to reduce some boilerplate. The futurize
helper method requires an extends:
keyword argument but it feels like :div
is probably the most reasonable / common default in most cases?
If that's the case, couldn't it could just explicitly be the default if no extends:
argument is passed, so futurizing partials could just look like this (without having to declare extends: :div
every time):
<%= futurize partial: "some/thing" do %>
<!-- placeholder -->
<% end %>
A little tweak in the method signature of Futurism::Helpers#futurize
:
-- def futurize(records_or_string = nil, extends:, **options, &block)
++ def futurize(records_or_string = nil, extends: :div, **options, &block)
I can PR it if you agree :)
Right now we've assembled some configuration options in lib/futurism.rb
, but recently a separate Configuration class was introduced.
We should consider moving other bits of the config there, albeit that would probably be a breaking change
Hi, thx for your efforts and progress this gem shows!
However, if I include some third-party embeds (tweet, telegram post, etc), it doesn't work. Youtube does work btw, but youtube embeds don't require any additional js to be loaded.
Example of tweet embed:
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Apple enters the 5G market with new line of iPhones <a href="https://t.co/pcWqTWOy29">https://t.co/pcWqTWOy29</a></p>— Financial Times (@FT) <a href="https://twitter.com/FT/status/1316103751883726850?ref_src=twsrc%5Etfw">October 13, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
Telegram embeds are way prettier (but still don't work because it's a js call)
<script async src="https://telegram.org/js/telegram-widget.js?12" data-telegram-post="bloomberg/641" data-width="100%" data-userpic="false"></script>
Any ideas on how to make this all work?
Best.
p.s. If this is more like a feature request (and I didn't design a post like a feature request), I'm sorry for that.
In order to re-bind JS behavior (particularly JQuery plugins ;-)) a CustomEvent
should be emitted after morphing is done
This should be easily doable after morphing is done: https://github.com/julianrubisch/futurism/blob/master/javascript/futurism_channel.js#L33
I am trying to futurize a view that relies on helpers that are included only in the subclass of ApplicationController, futurism render's in the context of ApplicationController.
https://github.com/julianrubisch/futurism/blob/abb5bcf31c9cb381777b623c5f4768ddc8eda67d/lib/futurism/channel.rb#L28
Would you be open to having an override for the controller constant, potentially set on the connection
object? or is there a better way to handle this?
secondly, controller params are not available in the channel,
meaning that in my case the helper used in the partial being rendered by futurize that depends on params to get current_* will not work, are you able to suggest a way of rendering in the context of a custom controller with params?
Render a partial with futurize that uses a helper in a subclass of ApplicationController.
also, in the helper add a method that reads request params
Expect to be able to specify controller class
Expect to have access to params
Yes. When using futurism some things set up in the Application::Channel for ActionCable are not available to threads rendering the partials for Futurism. The best example of this is setting tenants in Acts_as_Tenant. To get things to work well in action cable you may do something like:
def subscribed ActsAsTenant.current_tenant = current_tenant current_tenant.switch! stream_from "my_channel" end
However, with Futurism inheriting from ActionCable::Channel::Base there is no way to modify channel level "things" with out doing some nasty monkey patching.
Modify channel.rb to inherate from Application::Channel instead.
This is howstimulus_reflex (https://github.com/stimulusreflex/stimulus_reflex/blob/9287e4cf036062e820dd3cad6cb5ba937d180147/app/channels/stimulus_reflex/channel.rb#L3) works and it seems to work well.
PR #132
If a page is turbolinks-cached, it will show it in a flash, then render the futurism helpers.
However, if cached, it would preferable to simply use the cached version and halt everything.
Cf https://github.com/turbolinks/turbolinks#detecting-when-a-preview-is-visible, http://www.modernmpa.com/turbolinks
Trying out Futurism on a fresh app I'm getting fatal errors that seem to be a class-loading issue with GlobalID.
NameError - uninitialized constant Futurism::Resolver::Resources::GlobalID
The backtrace points to:
[...]/3.0.0/gems/futurism-1.2.0.pre9/lib/futurism/resolver/resources.rb:92:in `resolved_models'
I notice in my Gemfile.lock I have globalid
at version 1.0.0
, which was added by default. If I downgrade and manually pin it to <= 0.6.0
instead, the issue is resolved.
Make futurism VC-proof
It would be nice to be able to futurize VC just like partials.
Currently, only ActiveRecord::Base
objects can be serialized for locals
. However, you can relatively easily create your own GlobalId-able serializer/deserializer by mixing in GlobalID::Identification
and implementing two methods. See https://github.com/rails/globalid#usage for more information.
Ideally, we'd support any GlobalId-able entity in the locals chain.
If the user specifies to broadcast_immediately: true
, then initiate a cable_ready.broadcast
after each resource is rendered in https://github.com/julianrubisch/futurism/blob/v0.7.2/lib/futurism/channel.rb#L18-L41
There might be an opportunity to replace debounceEvents
with the generalized debouncer from CableReady 5 (available since pre4) and slim down the bundle size a bit
I'm in the process of vetting futurism for use with Rails russian doll caching to solve some long-standing performance issues that I have an app that I've built out that has fairly deeply nested relationships (4 or 5 levels deep, some polymorphic) along with some fairly complex permission requirements currently implemented with CanCanCan.
It seems to me that pairing futurize and some judicious caching could allow extremely smart and lazy caching to significantly speed up page load times and greatly improve the user experience. Thank you this gem is amazing and I'm excited to see where it can go.
While working on evaluating whether this will work or not, I've developed an example app that might be helpful to use in further testing and refining futurism. You can find it at https://github.com/rickychilcott/futurism-auth-spike and can test it at https://sleepy-sea-13861.herokuapp.com/ (note I'm using free Heroku so spin up times initially will be slow). The app isn't that interesting, but to get a sense of the interesting bits please see:
The short explanation is that we lazily load several nested sets of resources, going to the database and randomly sleeping a certain amount of time to determine whether or not the "current user" has the permission to edit a given resource. The user/resource permission mapping is cached in Redis for 10 minutes so that future lookups of a given resource will only take the time to read from the cache. All partials are cached, but since they are really only generating <futurism-element>
tags, they are lazily rendered on the page sometimes quickly (i.e. from cache) or slower (i.e. newly generated server side).
While this app isn't a perfect test, it's good enough to highlight some of the challenges and areas to improve futurism.
While playing with this in "production", I've noticed a few things:
<futurism-element>
never resolves. I have no idea why, but perhaps some sort of retry after a default/settable interval would be good.cable_ready.broadcast
line being called after all of the resources are rendered.Some other things that would be nice:
<futurism-element>
on a timer or to use javascript to trigger a rerender (i.e. document.queryselector('furism-element').reRender()
.To fix the 2nd item on the "things I've noticed" list, I wonder if we could move the cable_ready.broadcast
line into the loop. For quickly generating partials, this would have likely a small performance hit. For slower generating partials, it will have a significantly improved user experience.
For the other items, this is largely a set of discussion points that I'm open to working through, so I don't have a solution. However, as I've thought about these things, it feels like many of them could be made easier if instead of replacing <futurism-element>
using cable ready's outer_html
operating, what if we used inner_html and kept the <futurism-element>
around for the entire duration of the page.
We'd likely need to clean up or change the API to remove classes/styling after the server side render is complete, but it may make caching, rerendering, and even #57 could be made simpler by not caching the previous <futurism-element>
but instead just resetting it back to the pre-rendered state and setting element.innerHtml = ''
. I also wonder if we could build out a small function API to allow things like element.reset()
, element.render()
, element.cache(1000*60*60)
, element.reRender()
, element.reRender(force: true)
, element.reRender(every: 1000*60*5)
, etc.
Also, note that web components do allow you to implement a shadow DOM, so some of the "state" might be able to be kept within the component itself instead of localStorage, sessionStorage, etc. I don't fully understand whether or not turbolinks snapshotting or how styling is affected by the shadow DOM.
I'm happy to submit PRs (or ideally pair) for these items to get something started, but perhaps I've missed something in implementation details or specific reasons that some of these approaches haven't been taken. I love this gem and what it will enable, so I'm not intending any criticism. Just trying to help!
Noticed that extends: :li doesn't load on iPad
Maybe moving away from custom built in elements to a dynamically styled shadow DOM (display: table-row
) is the way to go in the future
when caching partials, it can happen that the Rails auto-expiration of SGIDs (https://github.com/rails/globalid) after 1 month leads to futurized partials not resolving if the SGIDs have expired.
we should set the SGIDs to never expire, and/or make that configurable
When using CableReady's updates_for
mechanism in combination with futurism, you often don't want to render the placeholders after triggering an update. The only way to do this right now is manually by adding a conditional:
<% if bypass_futurism? %>
<%= render ... %>
<% else %>
<%= futurize ... %>
<% end %>
The proposed API looks like this:
<%= futurize ... unless: bypass_futurism? %> ... <% end %>
In fact everything is already present from the "skip in test" feature, so this should be a quick one.
Retry to resolve a <futurism-element>
after a configurable delay.
I'm starting to implement Futurism in my project and I'm running into a bunch of snags. I'm going to document them here and then submit separate PRs as soon as I can.
We are currently using the Rails.application.routes.recognize_path
function and only passing the path
. My application uses subdomains, and as such the routes aren't properly recognized. If we pass the full url
it does a better job at matching the route.
We should ALSO rescue from ActionController::RoutingError
(and pass an empty hash) in case it's not recognized and possibly warn in the logs to help users understand why their path params might not be there. These routing errors are only likely to occur on non-get requests, and we could optionally loop through method: :put
, method: :post
, method: :delete
to nearly guarantee a recognized route. Do note that some routes (with proc/lambda-based constraints, for example) will never be recognizable
rails/rails#2656 has some details and rails/rails#21477 has a note from rafaelfranca saying recognize_path
is private API.
Have an app with subdomain constraints
Recognize ALL routes, or at least as many as possible.
sometimes you don't want a placeholder at all for a given element, so ideally a block would not be required as a placeholder.
In order to add custom functionality to Futurism, for say a Stimulus controller, it would be great to be able to pass data attribute through html_options and have them merge with data-signed-params, currently any passed data attributes wipe this out
There's not enough context to style placeholder content when futurizing collections.
When futurizing a collection, I'd like the futurize
method to yield arguments to the block for things like current item and index which could be used to further style the placeholder(s).
It might look something like this:
<%= futurize partial: "users/user", collection: users, extends: "tr" do |user, index| %>
Use deep_transform_values
to convert any ActiveRecord stored in locals
to a GID
We want to keep over-the-wire size as small as possible. MessageVerifier
will serialize a complete ActiveRecord otherwise, making the resulting string huge.
This will require to depend on Rails >= 6
Could not execute command from ({"command"=>"message", "identifier"=>"{"channel":"Futurism::Channel"}", "data"=>"{"sgids":[null]}"}) [NoMethodError - undefined method map' for nil:NilClass]: /usr/local/bundle/ruby/2.5.0/gems/futurism-0.2.0/lib/futurism/channel.rb:10:in
receive' | /usr/local/bundle/ruby/2.5.0/gems/actioncable-6.0.3.2/lib/action_cable/channel/base.rb:268:in public_send' | /usr/local/bundle/ruby/2.5.0/gems/actioncable-6.0.3.2/lib/action_cable/channel/base.rb:268:in
dispatch_action' | /usr/local/bundle/ruby/2.5.0/gems/actioncable-6.0.3.2/lib/action_cable/channel/base.rb:170:in block in perform_action' | /usr/local/bundle/ruby/2.5.0/gems/activesupport-6.0.3.2/lib/active_support/notifications.rb:182:in
instrument'
Question or potentially feature request.
It seems that futurism triggers a partial load on becoming visible, which is great for lazy loading say tab panels.
Is there a way that I can eager trigger futurism without toggling the containing elements visibility?
This would be useful async loading of expensive tab content
See example - Tab D is currently lazy loading on click/panel visibility, I would like to have that tab eagerly trigger futurism to provide an async loading of the panel content.
Futurism doesn't work if the javascript_pack_tag
is async
:
<%= javascript_pack_tag 'application', async: true %>
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.