denisdefreyne / cri Goto Github PK
View Code? Open in Web Editor NEWA tool for building commandline applications
License: MIT License
A tool for building commandline applications
License: MIT License
On https://rubygems.org/gems/cri, the links to "Documentation" and to "Homepage" both still point to README.md
and result in 404's.
Would love to see feature for parsing, validation and transformation of options.
Something like delegate
of Cri::OptionParser
but exposed to be used.
Some food for thought
option :p, :port, 'port number' do |val, _cmd|
Integer(val)
end
or even part of call to option
option :p, :port, 'port number', Integer do |val, _cmd|
val.is_a? Integer => true
end
or
required :c, 'config-file', 'configuration file' do |val, _cmd|
Configuration.new(val)
end
And validation
required :n, :number, 'some number' do |val, _cmd|
raise SomeException if val.to_i) > 5
end
Arguments could also be validated. Some way to say, this command expects one positional argument, if not there, command is not executed and error message shown.
When you combine options, and the last option has an argument, the argument is ignored.
For example, I think the following code should run fine, but results in an error:
def test_combined_options_with_argument
input = %w( foo -ab xxx bar )
definitions = [
{ :long => 'aaa', :short => 'a', :argument => :forbidden },
{ :long => 'bbb', :short => 'b', :argument => :required },
]
parser = Cri::OptionParser.parse(input, definitions)
assert(parser.options[:aaa])
assert_equal({ :bbb => 'xxx' }, parser.options)
assert_equal(%w(foo bar), parser.arguments)
end
error:
Cri::OptionParserTestCase#test_combined_options_with_argument:
Cri::OptionParser::OptionRequiresAnArgumentError: b
/Users/bart/Code/ruby/cri/lib/cri/option_parser.rb:230:in `block in handle_dash_option'
/Users/bart/Code/ruby/cri/lib/cri/option_parser.rb:223:in `each'
/Users/bart/Code/ruby/cri/lib/cri/option_parser.rb:223:in `handle_dash_option'
/Users/bart/Code/ruby/cri/lib/cri/option_parser.rb:173:in `run'
/Users/bart/Code/ruby/cri/lib/cri/option_parser.rb:105:in `parse'
/Users/bart/Code/ruby/cri/test/test_option_parser.rb:289:in `test_combined_options_with_argument'
Great gem! I've been using cri
on a couple projects and it's been working great. A user reported a bug on my rspec_n gem (which uses cri
) and stated rspec_n breaks awesome_print when both gems are listed in a Gemfile
. I traced the issue back to the colored
gem that cri
uses and thought I would make sure you were aware (especially since awesome_print is such a popular gem).
colored
adds colorize
, green
, red
, etc... methods directly on String. awesome_print
does the same thing. The gem that gets listed last in a Gemfile has their implementation loaded by Bundler. If colored
is listed last, it breaks awesome_print
.
Based on this comment in the awesome_print
code, awesome_print seems to have compatibility with the colorize gem (an alternative to colored), but it doesn't seem to know about colored.
If the terminal doesn't support colored output, it should not be shown.
This is mostly a problem on Windows.
Is it possible to make sub-commands accept -h even if the wrong number of arguments to it are provided? Right now I just get the wrong number of arguments, but -h/--help should be there to help them find out what arguments are needed I would think?
Is there some extended example/documentation how to use Cri::CommandRunner? I want to build a command in a class.
It clear it will call run
. But where arguments coming from? How this class can be loaded/declared?
Thanks in advance.
Kirby
Is it possible to implement something like command --option subcommand --another-option
?
There is an option in my design that is "common" to all subcommands, for me it makes sense to be passed to the root command instead of repeating its definition in all subcommand blocks. For sure I could use a helper, but the pattern I am proposing looks better to me. Thanks in advance.
On Ruby 1.8.7, order for Hash#each_pair
is not deterministic, which can lead to the order of options
and options for #{supercommand}
in the help output to intermittently switch. I found out about this at puppetlabs/r10k#26 (comment) . When building Cri::Command#help
, each_pair
which can cause the order to switch.
Ruby 1.9 has consistent hash ordering that's based on insertion so this only affects Ruby 1.8.7. If Ruby 1.8.7 is fully supported I would be very happy to fix this, although testing this could prove difficult because of the nondeterministic duplication of the issue.
In Nanoc, nanoc
is a shorthand for nanoc compile
. The behavior of nanoc
differs slightly from nanoc compile
, as options are not passed through, and Cri has no method of doing so.
Suggestion: Let Cri support default subcommand properly, e.g. through
default_subcommand :compile
It would be super useful to be able to split positional arguments using a --
. This is often used to disambiguate positional arguments, for example:
git checkout origin/master # branch master on origin
git checkout -- origin/master # local file master in origin directory
In my case, I'd like to split arbitrary arguments:
mycommand foo bar -- baz -- qux
The ideal backwards-compatible way to implement this is probably to make the arguments list a subclass of Array, with an accessor for getting groups of arguments. In the above example:
mycommand.arguments
=> ['foo', 'bar', 'baz', 'qux']
mycommand.argument_groups
=> ['foo', 'bar'], ['baz'], ['qux']
Or something. I'm not 100% sold on the accessor name :)
I'm trying to use multiple arguments and I have a option
block that looks like this:
option :g, :groups, "specify the groups", argument: :required, multiple: true do |value, cmd|
puts value
end
It works fine if argument: :required
or argument: optional
is present. If I remove that, value
is just an array of true
elements. Should argument:
be present when using multiple:
or is this a bug? I couldn't figure it out from the docs.
For example, my nanoc compile
now has a --preprocess
option.
(pending)
This might be caused by Set#merge
, which modifies the object, unlike Hash#merge
.
Is there a way to exec the run block from a subcommand ? for instance
if I'm in a subcommand to do something like
run do |opts, args, cmd|
cmd.supercommand.run(opts)
end
thanks in advance for any help
When using 2.15.8+ with nanoc and a port is specifed, nanoc errors out:
$ bundle exec nanoc live -p 3005
#<Thread:0x00007fda5490b380@/Users/ash/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/guard-nanoc-2.1.6/lib/guard/../nanoc/cli/commands/live.rb:22 run> terminated with exception (report_on_exception is true):
/Users/ash/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/nanoc-4.11.5/lib/nanoc/cli/commands/view.rb:34:in `fetch': key not found: :host (KeyError)
from /Users/ash/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/nanoc-4.11.5/lib/nanoc/cli/commands/view.rb:34:in `run'
from /Users/ash/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/guard-nanoc-2.1.6/lib/guard/../nanoc/cli/commands/live.rb:26:in `block in run'
Captain! We’ve been hit!
KeyError: key not found: :host
0. /Users/ash/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/nanoc-4.11.5/lib/nanoc/cli/commands/view.rb:34:in `fetch'
1. /Users/ash/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/nanoc-4.11.5/lib/nanoc/cli/commands/view.rb:34:in `run'
2. /Users/ash/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/guard-nanoc-2.1.6/lib/guard/../nanoc/cli/commands/live.rb:26:in `block in run'
A detailed crash log has been written to ./crash.log.
Looking into the problem, I created the following which takes the Cri defintions straight from https://github.com/nanoc/nanoc/blob/4.11.5/nanoc/lib/nanoc/cli/commands/view.rb#L3-L15
nanoc_example.rb
#!/usr/bin/env ruby
require 'cri'
require 'nanoc'
require 'nanoc/cli'
command = Cri::Command.define do
summary 'start the web server that serves static files'
description <<~EOS
Start the static web server. Unless specified, the web server will run on port
3000 and listen on all IP addresses. Running this static web server requires
`adsf` (not `asdf`!).
EOS
required :H, :handler, 'specify the handler to use (webrick/mongrel/...)'
required :o, :host, 'specify the host to listen on (default: 127.0.0.1)', default: '127.0.0.1'
required :p, :port, 'specify the port to listen on (default: 3000)', transform: Nanoc::CLI::Transform::Port, default: 3000
flag :L, :'live-reload', 'reload on changes'
no_params
run do |opts, args|
p opts.fetch(:port)
end
end
command.run(ARGV)
Gemfile
source "https://rubygems.org"
gem 'cri'
gem 'nanoc'
2.15.7
$ bundle exec ./nanoc_example.rb
3000
2.15.9
$ bundle exec ./nanoc_example.rb
bundler: failed to load command: ./nanoc_example.rb (./nanoc_example.rb)
KeyError: key not found: :port
/private/tmp/nanoc_example.rb:22:in `fetch'
/private/tmp/nanoc_example.rb:22:in `block (2 levels) in <top (required)>'
/Users/ash/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/cri-2.15.9/lib/cri/command.rb:360:in `run_this'
/Users/ash/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/cri-2.15.9/lib/cri/command.rb:296:in `run'
/private/tmp/nanoc_example.rb:26:in `<top (required)>'
Edit: Updated to reflect issue is present since 2.15.8+ and final example was for 2.15.9.
While testing I found that if we specify a wrong command or option the CRI code calls exit(). Shouldn't that decision be taken by the program instead of CRI. I am planning to use CRI for a my application that needs a command loop and cant afford to exit on wrong input. Right now Im overriding the exit method to avoid program termination, but I dont think that is an elegant workaround
Idea: support parameter definitions for more than one argument. For example:
param :host
param :port, transform: method(:Integer)
param :filenames, varargs: true
varargs: true
defines the parameter as corresponding to zero or more arguments.
For the time being, allowing it only as the last defined parameter is probably fine.
Hi!
This is really a minor issue, but I noticed a warning when building the doc:
[warn]: @param tag has unknown parameter name: The
in file `lib/cri/command.rb' near line 96
Indeed, line 93 of lib/cri/command.rb, the name for the @param is missing.
Cédric
The #skip_option_parsing
is good as far as it goes, but (IMHO) it addresses only half of the issue.
As far as I can tell, there is no way to define a command that will accept ab arbitrary number of arbitrary parameters without trying to treat them as subcommands. For examine, I don't see any way to do this:
configure set -v user=foo server=http://example.com/
because it gritches about user=foo
not being a valid subcommand.
Is there a trick to this, or is it actually not currently supported?
Thanks!
Since --
is used to separate argument groups and stop option parsing, using both at the same time can lead to conflicting requirements.
Imagine having rm -f -- -blah
to delete the file “-blah”. The --
stops option parsing and starts a new argument group. Currently, cri only creates a new argument group if the previous one is not empty. So the argument groups in this case will be
[
[ '-blah' ]
]
which is expected and good.
However, in the case of git checkout -- blah
, the argument_groups
will be
[
[ 'blah' ]
]
and not, as expected,
[
[],
[ 'blah' ],
]
The latter is necessary, because there is no way to disambiguate the blah
argument otherwise.
A solution would be to remove the empty argument group check.
CC @bobthecow
See #52.
The current implementation of default values only works for optional options, and the default value will not be used if the option is not provided. This is counter-intuitive; the default value should be the one that is used even when the option is not specified.
I have an involved command suite defined using x = Cri::Command.define
and x..add_command()
.
One of the subcommands needs to be able to accept arbitrary arguments and options to be passed through to a shell script. Usually, this is done by using '--' to signal 'don't process the rest of this line.'
I can see getting access to the unprocessed bits using the #unprocessed_arguments_and_options
attribute, but I can't figure out how to spot --
in order to stop the parser and leave the rest of the commandline accessible that way. Nor can i find any examples or documentation on how to do it.
The parse/processing is begun with root_command.run(ARGV)
Is the mechanism actually in there somewhere and I'm just too thick to find it?
Thans!
The README explains how to pass arguments to the command https://github.com/ddfreyne/cri#argument-parsing using param
, but it looks like we have to name each argument. Is it possible to have a command accept multiple arguments? I'd like to be able to do something like
my_command arg1 arg2 argN
Long option descriptions are not reflowed and automatically wrap, leading to ugly results.
Cri creates really nicely formatted help output. Since it already does indenting so nicely, is there currently any way to control line-wrapping, so options with long summaries don't go past, say, the 80th column, but are wrapped and indented instead? (Substitute 'a configurable' for '80th'.)
Thanks!
I am trying to build a set of subcommands in a kind of modular way, something like:
class Whatever
def initialize
@root = self.class.root
@root.add_command(self.class.subcommand)
end
def config
@root
end
private
def self.root`
subcommand = Cri::Command.define do
...
end
subcommand
end
def self.subcommand`
subcommand = Cri::Command.define do
...
end
subcommand
end
end
I would like to do some initializations in the main class and pass parameters to the methods to make them available to the command definitions. Cri::Command.define could use an additional parameter (it has three now) that is an arbitrary hash. Not sure if this is the intended usage, or maybe I am missing a pattern to build a subcommand set in a modular fashion.
r10k: Runtime error: #<ArgumentError: R10K::Action::Puppetfile::Install cannot handle option 'help'>
Error while running: #<RuntimeError: r10k could not install all required modules>
When passing both --help
and --verbose
, the --verbose
option has no effect on the help output:
[..]
COMMANDS
[..]
update update the data stored by the data source to a newer version
view start the web server that serves static files
(5 hidden commands ommitted; show them with --verbose)
[..]
The colours are nice but they tend to hurt my eyes after a while.
It would be nice to be able to theme/change the colours to my liking and/or disable them. Possibly by using a switch.
I tried working on this a little while, but I'm still getting used to ruby so I probably made a mess. Maybe I'll send a pull request later unless you have an elegant solution in mind.
command.run(ARGS) should be command.run(ARGV), no?
probot :: Projects/Personal/testing % ruby critest.rb
critest.rb:29:in `<main>': uninitialized constant ARGS (NameError)
When using the basic help command, the way the --verbose
option is currently presented can be a bit confusing for users:
$ mycommand help new
<snip>
OPTIONS FOR NEW
-d --debug Enable debug output.
-h --help Show help for this command.
-v --verbose show more detailed help
--version Show version of mycommand.
It would be clearer if --verbose
was listed in it's own section labelled OPTIONS FOR HELP
or something similar:
$ mycommand help new
<snip>
OPTIONS FOR NEW
-d --debug Enable debug output.
-h --help Show help for this command.
OPTIONS FOR HELP
-v --verbose show more detailed help
Idea: allow parameters to be defined with a default value. For example:
param :host
param :port, transform: method(:Integer), default: 3000
For simplicity, allow parameters with a default value to only appear at the end. The following would therefore not be possible:
param :foo, default: 'xxx'
param :bar
param :foo
param :bar, default: 'xxx'
param :qux
param :abc, default: 'yyy'
I image that this restriction will be fine, because the CLI will become complicated to use otherwise anyway.
The latest release drops support for Ruby 2.3. That's fine, but it was marked as 2.15.4
which breaks semver. That means that projects that use cri are now broken. For example, https://github.com/puppetlabs/r10k/blob/a84c7072e007042cd7926c298445b6c1a1c0141c/r10k.gemspec#L21-L26
This release should be pulled and rereleased as 2.16.0
.
From the option description, is it not clear whether an argument is required or not.
Actual:
-f --format define the output format
Expected:
-f --format=[value] define the output format
When running r10k puppetfile check
, this library cast an exception
Version installed cri-2.13.0
Version working cri-2.12.0
r10k version installed 3.0.0
Traceback (most recent call last):
13: from /usr/local/bin/r10k:23:in `<main>'
12: from /usr/local/bin/r10k:23:in `load'
11: from /var/lib/gems/2.5.0/gems/r10k-3.0.0/bin/r10k:3:in `<top (required)>'
10: from /usr/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'
9: from /usr/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'
8: from /var/lib/gems/2.5.0/gems/r10k-3.0.0/lib/r10k/cli.rb:3:in `<top (required)>'
7: from /usr/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'
6: from /usr/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'
5: from /var/lib/gems/2.5.0/gems/r10k-3.0.0/lib/r10k/cli/ext/logging.rb:1:in `<top (required)>'
4: from /usr/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'
3: from /usr/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'
2: from /var/lib/gems/2.5.0/gems/cri-2.13.0/lib/cri/command_dsl.rb:3:in `<top (required)>'
1: from /var/lib/gems/2.5.0/gems/cri-2.13.0/lib/cri/command_dsl.rb:6:in `<module:Cri>'
/var/lib/gems/2.5.0/gems/cri-2.13.0/lib/cri/command_dsl.rb:9:in `<class:CommandDSL>': uninitialized constant Cri::Error (NameError)
Did you mean? IOError
Errno
I am not sure if this is a r10k issue or cri issue.
Due to the text reflow in the help output, single newlines are ignored, double newlines are used to indicate the start of a new paragraph and paragraphs are joined with a double newline. This means it's not possible to use a line break in the description without introducing an empty line. When using lists, this results in ugly output:
This command has 3 features:
- awesome feature 1
- awesome feature 2
- awesome feature 3
We also have a lot of options.
preferred output:
This command has 3 features:
- awesome feature 1
- awesome feature 2
- awesome feature 3
We also have a lot of options.
A simple command like this:
require 'cri'
c = Cri::Command.define do
name 'test'
#summary 'foo'
run do |opts, args, cmd|
puts cmd.help
end
end
c.run(ARGV)
Will not output anything:
$ ruby wat.rb
Once I uncomment the summary
call, then it starts showing. I understand that summaries are useful for the end-user, but it would be nice if it at least showed the command name (instead of a blank line).
I'd like my application to be able to have an option set, without it being visible to regular users. A be_hidden
variant as already exists for commands would be helpful here.
I'd like to have a --force
option with no shorter version, but I can't figure out how to do it.
I've had the desire to have multiple long-form names for an option, as well.
Both of these might be addressed by being able to pass the option names as an array, and
short_form = options.find { |o| o.to_s.length == 1 }
long_forms = options - [ short_form ]
or something like that.
Thanks!
Hi Denis,
I tried to add the Inch Pages badge to your README, but unfortunately my AsciiDoc-fu is not strong enough.
I tried image::http://inch-pages.github.io/github/ddfreyne/cri.png
, but that did not seem to render right. Would mind adding this one yourself?
The badge looks like this (looking good!) and the URL of the Inch Page would be http://inch-pages.github.io/github/ddfreyne/cri/
Cri should provide a way to ensure that long and short options don’t clash between subcommand and supercommand.
Due to changes introduced in cri 2.12, my way of iterating over arguments no longer works. (This might very well be a bug in my code and not in cri).
My code extending the commandRunner:
# Returns an input iterator to use for the request.
# - if arguments are given, uses arguments
# - if the input file option is given, uses file input
# - if none of the previous are given, uses stdin
def input_iterator
return arguments.each unless arguments.empty?
return IO.foreach(options[:input]) if options[:input]
$stdin.each_line
end
This now throws a LocalJumpError, probably because the array was swapped for an argument_list in the underlying code:
LocalJumpError: no block given (yield)
/Users/bart/.rvm/gems/ruby-2.6.0/gems/cri-2.12.0/lib/cri/argument_list.rb:39:in `block in each'
/Users/bart/.rvm/gems/ruby-2.6.0/gems/cri-2.12.0/lib/cri/argument_list.rb:39:in `each'
/Users/bart/.rvm/gems/ruby-2.6.0/gems/cri-2.12.0/lib/cri/argument_list.rb:39:in `each'
/Users/bart/Code/Rails/unipept-cli/lib/commands/unipept/api_runner.rb:41:in `input_iterator'
/Users/bart/Code/Rails/unipept-cli/lib/commands/unipept/api_runner.rb:116:in `run'
/Users/bart/.rvm/gems/ruby-2.6.0/gems/cri-2.12.0/lib/cri/command_runner.rb:34:in `call'
/Users/bart/.rvm/gems/ruby-2.6.0/gems/cri-2.12.0/lib/cri/command_dsl.rb:257:in `block in runner'
/Users/bart/.rvm/gems/ruby-2.6.0/gems/cri-2.12.0/lib/cri/command.rb:337:in `run_this'
/Users/bart/.rvm/gems/ruby-2.6.0/gems/cri-2.12.0/lib/cri/command.rb:275:in `run'
/Users/bart/.rvm/gems/ruby-2.6.0/gems/cri-2.12.0/lib/cri/command.rb:293:in `run'
Do you have a suggestion on how to resolve this?
Please add a way to specify the default value for an option in a way that gets exposed to --help
, e.g.:
optional nil, :source, 'The source repository to load templates from.', default: '[email protected]:puppetlabs/modulesync_configs.git'
options:
-h --help show help for this command
--summary The source repository to load templates from.
Default value: [email protected]:puppetlabs/modulesync_configs.git
instead of
optional nil, :source, 'The source repository to load templates from.'
run do |opts, args, cmd|
source = opts.fetch(:source, '[email protected]:puppetlabs/modulesync_configs.git')
Help flag takes a block and then we have a run block which executes the command. If the command has --help option specified, the help block executes and then the run block also executes. Ideally if help option is specified then run block should not execute.
Again, coming from my application which is using Cri in a command loop. I cannot exit in the help block and have no control in Cri to stop execution of run block. Currently Im checking the presence of help option inside run block and just return if it is present.
/usr/local/share/gems/gems/r10k-2.6.0.dev/lib/r10k/cli.rb:9:in `command': /usr/local/share/gems/gems/cri-2.9.1/lib/cri/command.rb:46: syntax error, unexpected ')' (SyntaxError)
def initialize(is_error:)
Trying to split my command up in to separate files with the sub-commands each in their own file. However, this seems to not work as you can't pass in root_cmd when you require (so you could do the add_command in each file) and if you set a variable in the sub-command file it won't exist in the root_cmd file...
So, is it possible to create classes instead, or am I missing something else obvious?
Having this command and subcommand configuration:
base_cmd = Cri::Command.define do
name 'base_cmd'
usage 'base_cmd [options]'
summary 'does stuff'
description 'This command does a lot of stuff. I really mean a lot.'
flag :a, :aaaa, 'do even more stuff'
end
level1_cmd = Cri::Command.define do
name 'level1_cmd'
usage 'level1_cmd [options]'
summary 'does stuff'
description 'This command does a lot of stuff. I really mean a lot.'
end
level2_cmd = Cri::Command.define do
name 'level2_cmd'
usage 'level2_cmd [options]'
summary 'does stuff'
description 'This command does a lot of stuff. I really mean a lot.'
run do |opts, args, cmd|
puts opts.to_s
end
end
level1_cmd.add_command(level2_cmd)
base_cmd.add_command(level1_cmd)
base_cmd.run(ARGV)
Note that there is a flag a
configured on the base command base_cmd
and lowest level subcommand prints in stdout available opts
I find this behavior unexpected:
$ base_cmd -a level1_cmd level2_cmd
{}
$ base_cmd level1_cmd -a level2_cmd
{:aaaa=>true}
$ base_cmd level1_cmd level2_cmd -a
{:aaaa=>true}
Is this the expected behavior?
Currently, forbidden optional parameters set to nil
when not present in command line. From documentation, it is expected to return false
option :f, :force, 'push with force', argument: :forbidden
run do |opts, args, cmd|
puts "Force? #{opts[:force].inspect}!"
end
% ./push
Force? nil!
% ./push --force
Force? true!
The ordering of commands in nanoc help
is not alphabetical. The hidden commands appear at the bottom:
check run issue checks
compile compile items of this site
[...]
update update the data stored by the data source to a newer version
view start the web server that serves static files
autocompile start the autocompiler
validate-css validate the site’s CSS
validate-html validate the site’s HTML
validate-links validate links in site
watch start the watcher
Currently, --help --verbose
does not print the help verbosely, because both options are independent and do not know about each other.
Not sure how to solve this.
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.