ms-ati / docile Goto Github PK
View Code? Open in Web Editor NEWDocile keeps your Ruby DSLs tame and well-behaved
Home Page: http://ms-ati.github.com/docile/
License: MIT License
Docile keeps your Ruby DSLs tame and well-behaved
Home Page: http://ms-ati.github.com/docile/
License: MIT License
Hi @ms-ati ,
I have an enhancement request about backtrace.
When a NoMethodError
is raised inside the given block, the 1st entry of its back trace is not the original location.
For example,
require 'docile'
foo = Object.new
bar = Object.new
Docile.dsl_eval(foo) do
Docile.dsl_eval(bar) { fizz_buzz }
end
Execute above code then you can get following error message.
C:/Ruby/Ruby25-x64/lib/ruby/gems/2.5.0/gems/docile-1.3.1/lib/docile/fallback_context_proxy.rb:55:in `block in initialize': undefined method `fizz_buzz' for main:Object (NoMethodError)
from C:/Ruby/Ruby25-x64/lib/ruby/gems/2.5.0/gems/docile-1.3.1/lib/docile/fallback_context_proxy.rb:84:in `method_missing'
from C:/Ruby/Ruby25-x64/lib/ruby/gems/2.5.0/gems/docile-1.3.1/lib/docile/fallback_context_proxy.rb:55:in `block in initialize'
from C:/Ruby/Ruby25-x64/lib/ruby/gems/2.5.0/gems/docile-1.3.1/lib/docile/fallback_context_proxy.rb:84:in `method_missing'
from docile_backtrace.rb:7:in `block (2 levels) in <main>'
from C:/Ruby/Ruby25-x64/lib/ruby/gems/2.5.0/gems/docile-1.3.1/lib/docile/execution.rb:26:in `instance_exec'
from C:/Ruby/Ruby25-x64/lib/ruby/gems/2.5.0/gems/docile-1.3.1/lib/docile/execution.rb:26:in `exec_in_proxy_context'
from C:/Ruby/Ruby25-x64/lib/ruby/gems/2.5.0/gems/docile-1.3.1/lib/docile.rb:43:in `dsl_eval'
from docile_backtrace.rb:7:in `block in <main>'
from C:/Ruby/Ruby25-x64/lib/ruby/gems/2.5.0/gems/docile-1.3.1/lib/docile/execution.rb:26:in `instance_exec'
from C:/Ruby/Ruby25-x64/lib/ruby/gems/2.5.0/gems/docile-1.3.1/lib/docile/execution.rb:26:in `exec_in_proxy_context'
from C:/Ruby/Ruby25-x64/lib/ruby/gems/2.5.0/gems/docile-1.3.1/lib/docile.rb:43:in `dsl_eval'
The entry for the original location is this but the top entry is for docile
's code.
docile_backtrace.rb:7:in `block (2 levels) in <main>'
I'd like to roll back the backtrace to the original entry for perspicuity like below.
docile_backtrace.rb:7:in `block (2 levels) in <main>': undefined method `fizz_buzz' for main:Object (NoMethodError)
from docile_backtrace.rb:7:in `block (2 levels) in <main>'
from C:/Ruby/Ruby25-x64/lib/ruby/gems/2.5.0/gems/docile-1.3.1/lib/docile/execution.rb:26:in `instance_exec'
from C:/Ruby/Ruby25-x64/lib/ruby/gems/2.5.0/gems/docile-1.3.1/lib/docile/execution.rb:26:in `exec_in_proxy_context'
from C:/Ruby/Ruby25-x64/lib/ruby/gems/2.5.0/gems/docile-1.3.1/lib/docile.rb:43:in `dsl_eval'
from docile_backtrace.rb:7:in `block in <main>'
from C:/Ruby/Ruby25-x64/lib/ruby/gems/2.5.0/gems/docile-1.3.1/lib/docile/execution.rb:26:in `instance_exec'
from C:/Ruby/Ruby25-x64/lib/ruby/gems/2.5.0/gems/docile-1.3.1/lib/docile/execution.rb:26:in `exec_in_proxy_context'
from C:/Ruby/Ruby25-x64/lib/ruby/gems/2.5.0/gems/docile-1.3.1/lib/docile.rb:43:in `dsl_eval'
It now appears that, in addition to being end-of-life for years, Ruby 1.8.7 is no longer runnable on Travis CI.
For comparison, Github Actions supports no Rubies older than 2.1.
Therefore, it's now time to remove testing, and then support, for Ruby 1.8. In Docile, this will result in actual code changes, as code paths and choices do exist solely to support 1.8 today.
I'd like to use this ticket to capture (a) plans and notes on what code can change to remove this support, and (b) links to every PR that pertains to removing this support. The final PRs should be for a new minor version.
Do you use Docile on an end-of-life ("EOL") Ruby version, such as 1.9.3? If so, please respond to this ticket to let me know. If nobody is actively following this project and using EOL Rubies, I'd like to consider removing support for all of them. However, I would prefer to continue to support any Rubies that are actually in use by folks following this project, so please respond if you would be impacted!
singleton_class
- stop using the 1.8.7 fallback patternFallbackContextProxy#instance_variables
- stop supporting Strings on 1.8docile.gemspec
and on_what.rb
- remove all pinned dependencies for 1.8The examples make it look easy but they are all examples when the DSL code lives in the same file as the DSL implementation, which is hardly useful.
How would I actually run it if
@sauce_level = :extra
pizza do
cheese
pepperoni
sauce @sauce_level
end
was in a separate file from the rest?
Testing with ruby 3.3.0dev (I tried: ruby/ruby@8cb906d ), docile git head ( 08cf67d ) rspec testsuite fails like:
$ rspec spec
.....
Failures:
1) Docile.dsl_eval when DSL have NoMethod error inside raise NoMethodError error from nil
Failure/Error:
expect { push_element }.
to raise_error(
NoMethodError,
/undefined method `push' (for|on) nil:NilClass/
)
expected NoMethodError with message matching /undefined method `push' (for|on) nil:NilClass/, got #<NoMethodError: undefined method `push' for nil> with backtrace:
# ./spec/docile_spec.rb:242:in `push_element'
# ./lib/docile/fallback_context_proxy.rb:93:in `method_missing'
# ./spec/docile_spec.rb:249:in `block (6 levels) in <top (required)>'
# ./spec/docile_spec.rb:249:in `block (5 levels) in <top (required)>'
# ./lib/docile/execution.rb:35:in `instance_exec'
# ./lib/docile/execution.rb:35:in `exec_in_proxy_context'
# ./lib/docile.rb:46:in `dsl_eval'
# ./spec/docile_spec.rb:248:in `block (4 levels) in <top (required)>'
# ./spec/docile_spec.rb:249:in `block (5 levels) in <top (required)>'
# ./lib/docile/execution.rb:35:in `instance_exec'
# ./lib/docile/execution.rb:35:in `exec_in_proxy_context'
# ./lib/docile.rb:46:in `dsl_eval'
# ./spec/docile_spec.rb:248:in `block (4 levels) in <top (required)>'
Finished in 0.03959 seconds (files took 0.10392 seconds to load)
45 examples, 1 failure
Failed examples:
rspec ./spec/docile_spec.rb:247 # Docile.dsl_eval when DSL have NoMethod error inside raise NoMethodError error from nil
This is due to :
ruby/ruby#6950
https://bugs.ruby-lang.org/issues/18285
I found strange behavior that DSL object is replaced when #ds_eval is nested.
Here is sample code to re-produce this issue.
require 'docile'
class Foo
def display
p 'foo'
end
end
class Bar
def display
p 'bar'
end
end
foo = Foo.new
bar = Bar.new
Docile.dsl_eval(foo) do
display # 1st
Docile.dsl_eval(bar) do
display # 2nd
end
display # 3rd
end
Execute above code, you will see following output:
$ ruby docile_test.rb
"foo"
"bar"
"bar"
The 3rd #display call should output "foo"
because foo
object is DSL object.
However it's seemed that DSL object of outer block is replaced with DSL object of innter block (bar
) because actual output is "bar"
.
Can't do this:
variable PARAMETER_VARIABLE.create {
name :bar
value {:alpha => {:default => 1}, :beta => {:default => 2}, :charlie => {:default => 3}, :delta => {:default => 4}}
}
for something like this:
class Parameter
attr_reader :name
def initialize(name, metadata)
@name = name
super({:name => name, :metadata => metadata})
end
def Parameter.create(&block)
Docile.dsl_eval(ParameterBuilder.new, &block).create
end
end
class ParameterBuilder
def name(name)
@name = name
end
def value(value)
if value.is_a? Hash
@value = {:child => value}
elsif value.is_a? Array
@value = {:default => value}
else
@value = value
end
end
def create
Parameter.new(@name, @value)
end
end
With support dropping for Ruby 1.8.7 in #58, is it time to instrument Rubocop in CI, and bring the entire codebase into line with its recommendations?
See modern-project/modern-ruby@e1eee26 where Docile was replaced with instance_eval
to avoid the leakage of an instance variable:
/usr/lib/ruby/gems/2.3.0/gems/docile-1.1.5/lib/docile/fallback_context_proxy.rb:57:in `method_missing': [DEPRECATION] #load_adapter is deprecated. Use #load_profile instead.
Full message on Ruby 2.7.5:
/ruby-2.7.5/gems/docile-1.4.0/lib/docile/fallback_context_proxy.rb:93:in `method_missing': [DEPRECATION] #adapters is deprecated. Use #profiles instead.
Happens possibly when using Simplecov, from 0.16.0 till newest.
Configure Dependabot to keep dependencies up to date.
Instance variables of DSL context object are overwritten with nil value when DSL conetext object and block context object are the same object.
Here is example code:
require 'docile'
class DSLContext
def foo(v = nil)
@foo = v if v
@foo
end
def process_code(code)
Docile.dsl_eval(self, &binding.eval("proc { #{code} }"))
end
end
dsl_context = DSLContext.new
dsl_context.process_code 'foo 1; p foo'
p dsl_context.foo
I think the result of above code should be like below:
1
1
but the actual result is below:
1
nil
Hi Marc!
I'm experimenting with some multi-threaded code that uses Docile and I think I may have encountered a thread safety issue. I'm able to reproduce with the following code (depends on the https://github.com/grosser/parallel gem):
require 'parallel'
require 'docile'
class MyClass
def run_in_threads
Parallel.each(
[ Array.new, Array.new ],
in_threads: 2
) { |array| Docile.dsl_eval_with_block_return(array, &block) }
end
def block
lambda { do_some_work }
end
def do_some_work
sleep(rand)
push 1
end
end
MyClass.new.run_in_threads
This pretty reliably gives me:
Traceback (most recent call last):
8: from /Users/mdiscala/.gem/ruby/2.6.6/gems/parallel-1.19.2/lib/parallel.rb:211:in `block (4 levels) in in_threads'
7: from /Users/mdiscala/.gem/ruby/2.6.6/gems/parallel-1.19.2/lib/parallel.rb:360:in `block in work_in_threads'
6: from /Users/mdiscala/.gem/ruby/2.6.6/gems/parallel-1.19.2/lib/parallel.rb:519:in `with_instrumentation'
5: from /Users/mdiscala/.gem/ruby/2.6.6/gems/parallel-1.19.2/lib/parallel.rb:361:in `block (2 levels) in work_in_threads'
4: from /Users/mdiscala/.gem/ruby/2.6.6/gems/parallel-1.19.2/lib/parallel.rb:510:in `call_with_index'
3: from (irb):237:in `block in run_in_threads'
2: from (irb):241:in `block in block'
1: from (irb):246:in `do_some_work'
NoMethodError (undefined method `push' for #<MyClass:0x00007fa66f90c9b0>)
When I run this with only a single thread (changing in_threads: 1
), the output comes as expected:
irb(main):273:0> MyClass.new.run_in_threads
=> [[1], [1]]
From my reading of the code, it seems that Docile achieves the ability for do_some_work
to call methods on the DSL object by adding a method_missing
call to the base class (see https://github.com/ms-ati/docile/blob/master/lib/docile/fallback_context_proxy.rb#L54). The gem then cleans up after itself after the calls are complete https://github.com/ms-ati/docile/blob/master/lib/docile/fallback_context_proxy.rb#L54.
My hunch is that I get the method missing error because the clean up happens in one thread and then removes the methods for the other one. I could also imagine getting into cases where the push
method ends up writing to the wrong array, depending on the timing. Is that understanding correct?
If so, it seems like this may be difficult to make thread safe. The best I can think of is to (1) assign the receiver to a thread variable in https://github.com/ms-ati/docile/blob/master/lib/docile/fallback_context_proxy.rb#L5, (2) have the method missing definition dispatch calls to the receiver pulled from the thread variable, and (3) never cleanup the method_missing definition.
This doesn't quite seem worth it to support to me, but am curious for your take on all of the above!
Happy holidays & hope all is well!
--Mike
Hi. Thanks for all your work!
I am the maintainer of https://github.com/avo-hq/avo. Avo is an admin panel framework where you can build apps very fast! It achieves that through an extendable and flexible DSL.
This is just a sample. The result of this you can visit here.
class PostResource < Avo::BaseResource
field :id, as: :id
field :name, as: :text, required: true, sortable: true
field :body, as: :trix, placeholder: "Enter text", always_show: false, attachment_key: :attachments, hide_attachment_url: true, hide_attachment_filename: true, hide_attachment_filesize: true
field :cover_photo, as: :file, is_image: true, as_avatar: :rounded, full_width: true, hide_on: []
field :cover_photo, as: :external_image, name: "Cover photo", required: true, hide_on: :all, link_to_resource: true, as_avatar: :rounded, format_using: ->(value) { value.present? ? value&.url : nil }
field :audio, as: :file, is_audio: true
field :excerpt, as: :text, hide_on: :all, as_description: true do |model|
ActionView::Base.full_sanitizer.sanitize(model.body).truncate 130
rescue
""
end
field :is_featured, as: :boolean, visible: ->(resource:) { context[:user].is_admin? }
field :is_published, as: :boolean do |model|
model.published_at.present?
end
field :user, as: :belongs_to, placeholder: "โ"
field :status, as: :select, enum: ::Post.statuses, display_value: false
field :comments, as: :has_many
filter PostStatusFilter
action TogglePublished
tool PostInfo
end
I'd like to change the way we build the DSL. At the moment we use static methods to get the field
s and build the final object.
Now, we're introducing wrappers like tab
or panel
.
class PostResource < Avo::BaseResource
panel "Main info" do
field :name, as: :text, required: true, sortable: true
field :body, as: :trix, placeholder: "Enter text", always_show: false, attachment_key: :attachments, hide_attachment_url: true, hide_attachment_filename: true, hide_attachment_filesize: true
end
tab "Files" do
field :cover_photo, as: :file, is_image: true, as_avatar: :rounded, full_width: true, hide_on: []
field :audio, as: :file, is_audio: true
end
end
I know that you usually have to give docile a block with the DSL, but is there a way to use docile and pick up the fields from the resource root class like above?
Thank you!
Hi there!
First, thanks a lot for your work! ๐
require "docile"
class Parent
attr_reader :children
def initialize
@children = []
end
def add_child(clazz, &block)
child = clazz.new
children << child
Docile.dsl_eval(child, &block) if block
child
end
end
class CustomParent < Parent
def do_stuff
add_child Parent do
wanna_be_custom
end
end
def wanna_be_custom
add_child CustomParent
end
end
parent = CustomParent.new
parent.do_stuff
p parent.children
Both Children are added to the main parent/within the block self isn't shifted to the child
[#<Parent:0x000055794759c598 @children=[]>, #<CustomParent:0x00005579475ab7a0 @children=[]>]
the main parent has one child, which itself then has the CustomParent as a child. (within the block self correctly is the child)
[#<Parent:0x000055d997c50460 @children=[#<CustomParent:0x000055d997c5b658 @children=[]>]>]
This output can currently be achieved in 2 ways:
wanna_be_custom
def do_stuff
add_child Parent do
add_child CustomParent
end
end
add_child
, instead of instantiating the passed in class just use self.class
(yes this changes semantics but might give a hint as to the source of this) def add_child(clazz, &block)
child = self.class.new
children << child
Docile.dsl_eval(child, &block) if block
child
end
Might be related to #31 but it's marked fixed ๐คทโโ๏ธ
edit: After a good night sleep I finally realized that of course this is about self being the wrong thing so much like #31 ;)
With experience of both, I've found maintenance of CI in Github Actions quite a bit easier than in Travis CI.
Therefore, when the day arrives that we support only Rubies supported in Github Actions, I'd like to plan to transition.
Hey, I was just looking over the code base here and I was pleasantly surprised at how small and easy to understand it is. There is one thing though that I'm a bit confused about.
On this line, you set the proxy context to a proxy of a proxy. Why is that? Why not just proxy the block context directly?
To make it easier for packaging tools to check the license, please add
s.license = 'MIT'
to your gemspec
Hi,
We're using Docile on Avo to manage some parts of our DSL.
We're experiencing an issue where a method call within the block is not executing in the desired context.
Here's a simplified example of the problem:
require "docile"
module HasItems
attr_reader :items
def initialize
@items = []
end
def field(id)
@items << id
end
end
class Panel
include HasItems
end
class Resource
include HasItems
def panel(&block)
panel = Docile.dsl_eval(Panel.new, &block)
puts "Panel items: #{panel.items}"
end
def extracted_fields
puts "Context inside extracted_fields method: #{self}"
field :extracted
end
def fields
field :id
panel do
puts "Context inside panel do block: #{self}"
extracted_fields
end
end
end
resource = Resource.new
resource.fields
puts "Resource items: #{resource.items}"
Result:
Context inside panel do block: #<Panel:0x00007fd830d3d488>
Context inside extracted_fields method: #<Resource:0x00007fd830d3d758>
Panel items: []
Resource items: [:id, :extracted]
Expected result (ignoring the context prints):
Panel items: [:extracted]
Resource items: [:id]
We'd like to make it as easy as possible to extract methods in the most natuarl way, as a method that they can run inside our custom blocks (panel
, tab
, sidebar
, and more).
When the panel
block it's executed the extracted_fields
method is not found, so it falls back to the @__fallback__
context which is the resource's context. The problem here is that the code from extracted_fields
was called from the panel
block with the intention to add a field to that panel, but it was added to the resource
.
We don't like this approach as it makes the user learn yeat another pattern that they might implement badly and doesn't feel that natural.
def extracted_fields(context)
puts "Context inside extracted_fields method: #{self}"
context.field :extracted
end
def fields
field :id
panel do
puts "Context inside panel do block: #{self}"
extracted_fields(self)
end
end
extracted_fields
return a Proc
and instance_exec
itIt seems unnatural again to have a method run a block. But mor importantly, they have to call instance_exec
inside the panel
block which we don't want
def extracted_fields
puts "Context inside extracted_fields method: #{self}"
-> { field :extracted }
end
def fields
field :id
panel do
puts "Context inside panel do block: #{self}"
instance_exec &extracted_fields
end
end
Both workarounds require public DSL interaction, and one of our goals is to keep the public DSL as simple as possible.
The ideal scenario would be that they declare the methods on the resource file and just rub them inside the custom blocks (panel
, tab
, sidebar
, and more).
def extracted_fields
field :extracted
end
def fields
field :id
panel do
extracted_fields
end
end
If that's impossible, we'd like to expose a simple method call or something similar that would be simple to implement and least destructive.
def extracted_fields
field :extracted
end
def fields
field :id
panel do
unpack_fields extracted_fields
# or maybe
unpack_fields -> { extracted_fields }
end
end
Is there a more elegant solution or best practice within Docile to ensure that method calls within a block execute in the expected context while avoiding the need for additional public DSL interactions?
We'd greatly appreciate your help and guidance on this matter.
Thank you!
In your full person tree example, it illustrates a very interesting bug. (Well, it may not be a bug, but it's definitely something I would like to be able to avoid.)
If we change the code to:
p = person {
puts "1a: #{self}"
name 'John Smith'
puts "1b: #{self}"
mother {
puts "2a: #{self}"
name 'Mary Smith'
}
puts "1c: #{self}"
father {
puts "3a: #{self}"
name 'Tom Smith'
mother {
puts "3a1: #{self}"
name 'Jane Smith'
}
puts "3b: #{self}"
}
puts "1d: #{self}"
}
Then, we would think all the "1x" selfs would be the same, all the 2x selfs would be the same, and so on. But ,they're not. You actually end up with:
1a: #<PersonBuilder:0x000000000331f260>
1b: #<PersonBuilder:0x000000000331f260>
2a: #<PersonBuilder:0x000000000331df28>
1c: #<PersonBuilder:0x000000000331df28>
3a: #<PersonBuilder:0x0000000003317e98>
3a1: #<PersonBuilder:0x00000000033164d0>
3b: #<PersonBuilder:0x00000000033164d0>
1d: #<PersonBuilder:0x0000000003317e98>
Which is a distinct problem. I run into this in one of the tests in DSL::Maker (a quasi-DSL for creating DSLs). I've read your code and I think the problem is here - it should avoid setting self
, but I'm not sure.
Hi @ms-ati ,
Following warning related to key word argument is displayed when an method including key word argument is called withing DSL block.
/opt/rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/docile-1.3.2/lib/docile/fallback_context_proxy.rb:87: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
Execute this example code then you will see the above warning.
class Foo
def bar(baz: 0)
end
end
require 'docile'
Docile.dsl_eval(Foo.new) do
bar(baz: 1)
end
Hi @ms-ati ,
I have a question about use of DSL object's private method.
I tried to call DLS object's private methods via Docile.dsl_eval
but I got NoMethodError.
Here is a small script to reproduce this problem.
require 'docile'
class Foo
private
def foo; end
end
Docile.dsl_eval(Foo.new) { foo } # => NoMethodError (undefined method `foo' for main:Object)
Is this correct behavior?
If you think that DSL methods should be public then I think this is correct behavior.
If you think that Docile.dsl_eval
should behave like #instance_eval
then I think this problem should be resolved.
See how this PR to bump Docile to 1.3.2 in the cfer
project breaks its specs:
https://travis-ci.org/seanedwards/cfer/builds/601409476
RuntimeError:
Expected a value or block when setting property DocileUndoFallback
# ./lib/cfer/block.rb:93:in `method_missing'
# /home/travis/.rvm/gems/ruby-2.5.5/gems/docile-1.3.2/lib/docile/fallback_context_proxy.rb:87:in `method_missing'
# /home/travis/.rvm/gems/ruby-2.5.5/gems/docile-1.3.2/lib/docile/execution.rb:29:in `exec_in_proxy_context'
# /home/travis/.rvm/gems/ruby-2.5.5/gems/docile-1.3.2/lib/docile.rb:43:in `dsl_eval'
# ./lib/cfer/block.rb:12:in `build_from_block'
# ./lib/cferext/aws/iam/policy_generator.rb:15:in `statement'
# ./lib/cferext/aws/iam/policy_generator.rb:20:in `allow'
# /home/travis/.rvm/gems/ruby-2.5.5/gems/docile-1.3.2/lib/docile/fallback_context_proxy.rb:87:in `method_missing'
# ./lib/cferext/aws/iam/policy_generator.rb:54:in `block in <module:IAM>'
# /home/travis/.rvm/gems/ruby-2.5.5/gems/docile-1.3.2/lib/docile/execution.rb:26:in `instance_exec'
# /home/travis/.rvm/gems/ruby-2.5.5/gems/docile-1.3.2/lib/docile/execution.rb:26:in `exec_in_proxy_context'
# /home/travis/.rvm/gems/ruby-2.5.5/gems/docile-1.3.2/lib/docile.rb:43:in `dsl_eval'
# ./lib/cfer/block.rb:12:in `build_from_block'
# ./lib/cferext/aws/iam/policy_generator.rb:48:in `generate_policy'
# ./lib/cferext/aws/iam/policy_generator.rb:53:in `<module:IAM>'
# ./lib/cferext/aws/iam/policy_generator.rb:5:in `<module:AWS>'
# ./lib/cferext/aws/iam/policy_generator.rb:4:in `<module:CferExt>'
# ./lib/cferext/aws/iam/policy_generator.rb:3:in `<top (required)>'
# ./lib/cferext/aws/iam/policy.rb:1:in `require'
# ./lib/cferext/aws/iam/policy.rb:1:in `<top (required)>'
# ./lib/cfer.rb:368:in `require'
# ./lib/cfer.rb:368:in `block in <top (required)>'
# ./lib/cfer.rb:368:in `each'
# ./lib/cfer.rb:368:in `<top (required)>'
# ./spec/spec_helper.rb:64:in `require'
# ./spec/spec_helper.rb:64:in `<top (required)>'
# ./spec/cfer_spec.rb:1:in `require'
# ./spec/cfer_spec.rb:1:in `<top (required)>'
docile-1.1.1/lib/docile/fallback_context_proxy.rb:57: warning: assigned but unused variable - e
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.