Giter Club home page Giter Club logo

ruby-figo's People

Contributors

02strich avatar achimsen avatar berend avatar dzdidi avatar feejai avatar javaes avatar leondu avatar mattes avatar mduqueoviedo avatar mrh-line avatar rknoll avatar steph186 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ruby-figo's Issues

Does not compile on Heroku

The installation of an app with the master branch of this gem in the Gemfile fails on heroku:

sh: 2: Syntax error: Unterminated quoted string sh: 2: Syntax error: Unterminated quoted string ! ! Could not detect rake tasks ! ensure you can run$ bundle exec rake -Pagainst your app ! and using the production group of your Gemfile. ! rake aborted! ! Bundler::GemRequireError: There was an error while trying to load the gem 'figo'. ! Gem Load Error is: No such file or directory @ rb_sysopen - /tmp/build_1f20d0bf6ddf341f63b62120a3db587f/decimogmbh-client-29150e6ea4c3cfae12e578fe40dc6d7754434a2a/vendor/bundle/ruby/2.3.0/bundler/gems/ruby-figo-257619ed8fb9/lib/../CONFIG.yml

It works locally though. Maybe check why your CI is failing - I assume it is the same problem

Clearer errors

In the lib, the HttpUnauthorized error is mapped as raise Error.new("unauthorized", "Missing, invalid or expired access token.")

However I have received this error in two cases when the situation is different:

  • When my developer key got a permission error, specifically when I tried to create a new figo user and my app was not authorised to do so.
  • When I invoke session.user over a previously removed user (but the session is valid).

In both cases I think the problem has nothing to do with the token and the error is a bit misleading.

create_user is not accepting 'send_newsletter' parameter

When I try to create a user with the create_user function I get the following error:
Figo::Error (Invalid parameter send_newsletter)

I dug a bit and tried a local version of the entire library, so after adding some logging I can show this:

Data hash

{"name"=>"Test testing", "email"=>"[email protected]", "password"=>"demo1234", "language"=>"en", "send_newsletter"=>true, "affiliate_client_id"=>"[HIDDEN]"}

Encoded URL

name=Test+testing&email=test%40test.com&password=demo1234&language=en&send_newsletter=true&affiliate_client_id=[HIDDEN]

Response

Completed 500 Internal Server Error in 5254ms

Figo::Error (Invalid parameter send_newsletter):
lib/figo.rb:88:in request' lib/figo.rb:140:inquery_api'
lib/figo.rb:211:in create_user' app/controllers/operations_controller.rb:18:increate_user'

As far as I understand, the backend of the API is not accepting send_newsletter parameter. I have tried to modify the source, let it by default, use "True", "true", "False", 0... None of them is accepted.
It only works if I don't send the parameter at all:
data = { 'name' => name, 'email' => email, 'password' => password, 'language' => language, 'affiliate_client_id' => @client_id}

I will submit a pull request so you can see the problem.

web_demo not functional

[2015-10-15 12:27:27] INFO  WEBrick 1.3.1
[2015-10-15 12:27:27] INFO  ruby 2.2.1 (2015-02-26) [x86_64-linux]
== Sinatra/1.4.5 has taken the stage on 3000 for development with backup from WEBrick
[2015-10-15 12:27:27] INFO  WEBrick::HTTPServer#start: pid=4074 port=3000
        SECURITY WARNING: No secret option provided to Rack::Session::Cookie.
        This poses a security threat. It is strongly recommended that you
        provide a secret to prevent exploits that may be possible from crafted
        cookies. This will not be supported in future versions of Rack, and
        future versions will even invalidate your existing user cookies.

        Called from: /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/builder.rb:86:in `new'.
Figo::Error - We are very sorry, but something went wrong.:
    /tmp/ruby-figo/lib/figo.rb:100:in `request'
    /tmp/ruby-figo/lib/figo.rb:252:in `query_api'
    /tmp/ruby-figo/lib/figo.rb:261:in `query_api_object'
    /tmp/ruby-figo/lib/figo.rb:380:in `transactions'
    app.rb:50:in `block in <main>'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:1603:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:1603:in `block in compile!'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:966:in `[]'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:966:in `block (3 levels) in route!'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:985:in `route_eval'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:966:in `block (2 levels) in route!'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:1006:in `block in process_route'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:1004:in `catch'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:1004:in `process_route'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:964:in `block in route!'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:963:in `each'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:963:in `route!'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:1076:in `block in dispatch!'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:1058:in `block in invoke'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:1058:in `catch'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:1058:in `invoke'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:1073:in `dispatch!'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:898:in `block in call!'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:1058:in `block in invoke'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:1058:in `catch'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:1058:in `invoke'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:898:in `call!'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:886:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rack-protection-1.5.3/lib/rack/protection/xss_header.rb:18:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rack-protection-1.5.3/lib/rack/protection/base.rb:49:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rack-protection-1.5.3/lib/rack/protection/base.rb:49:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rack-protection-1.5.3/lib/rack/protection/path_traversal.rb:16:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rack-protection-1.5.3/lib/rack/protection/json_csrf.rb:18:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rack-protection-1.5.3/lib/rack/protection/base.rb:49:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rack-protection-1.5.3/lib/rack/protection/base.rb:49:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rack-protection-1.5.3/lib/rack/protection/frame_options.rb:31:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/session/abstract/id.rb:225:in `context'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/session/abstract/id.rb:220:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/logger.rb:15:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/commonlogger.rb:33:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:217:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:210:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/head.rb:13:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/methodoverride.rb:22:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/show_exceptions.rb:21:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:180:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:2014:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:1478:in `block in call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:1788:in `synchronize'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:1478:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/handler/webrick.rb:88:in `service'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/2.2.0/webrick/httpserver.rb:138:in `service'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/2.2.0/webrick/httpserver.rb:94:in `run'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/2.2.0/webrick/server.rb:294:in `block in start_thread'
[2015-10-15 12:27:33] ERROR NoMethodError: undefined method `join' for #<String:0x0055d6fe6dbbb0>
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/show_exceptions.rb:37:in `rescue in call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/show_exceptions.rb:21:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:180:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:2014:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:1478:in `block in call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:1788:in `synchronize'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:1478:in `call'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/handler/webrick.rb:88:in `service'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/2.2.0/webrick/httpserver.rb:138:in `service'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/2.2.0/webrick/httpserver.rb:94:in `run'
    /home/aep/.rbenv/versions/2.2.1/lib/ruby/2.2.0/webrick/server.rb:294:in `block in start_thread'
localhost.localdomain - - [15/Oct/2015:12:27:33 CEST] "GET / HTTP/1.1" 500 340

Return transactions in order or provide a way to sort by transaction_id

Or provide a way to order transactions by transaction id.

According to the API doc, we can get all transactions back, and also we can provide a transaction_id so we can get transactions after the transaction_id.

Problem is when I get transactions back, they are not ordered by transaction id, like bellow:

account.transactions(last_transaction_id).map(&:transaction_id)
=> ["T1.72", "T1.73", "T1.74", "T1.75", "T1.76", "T1.77", "T1.78", "T1.79", "T1.64", "T1.65", "T1.66", "T1.67", "T1.68", "T1.69", "T1.70", "T1.71"]

And I am not sure how could I get the last transaction and how this transaction_id is generated, am I safe to order it by numbers after the T1. part? Will it change to something like T2.?

Figo::Error: We are very sorry, but something went wrong.

require 'figo'
session = Figo::Session.new('ASHWLIkouP2O6_bgA2wWReRhletgWKHYjLqDaqb0LFfamim9RjexTo22ujRIP_cjLiRiSyQXyt2kM1eXU2XLFZQ0Hro15HikJQT_eNeT_9XQ')
puts session.accounts.count

This unfortunately explodes with:

Figo::Error: We are very sorry, but something went wrong.
from /Users/bijan/.rbenv/versions/2.5.0-dev/lib/ruby/gems/2.5.0/gems/figo-1.4.0/lib/helpers/https.rb:48:in `request'
from /Users/bijan/.rbenv/versions/2.5.0-dev/lib/ruby/gems/2.5.0/gems/figo-1.4.0/lib/figo.rb:157:in `query_api'
from /Users/bijan/.rbenv/versions/2.5.0-dev/lib/ruby/gems/2.5.0/gems/figo-1.4.0/lib/figo.rb:166:in `query_api_object'
from /Users/bijan/.rbenv/versions/2.5.0-dev/lib/ruby/gems/2.5.0/gems/figo-1.4.0/lib/account/api_call.rb:7:in `accounts'
from (irb):3
from /Users/bijan/.rbenv/versions/2.5.0-dev/bin/irb:11:in `<main>'

This error seems to come from an internal server error on the figo side.
I tried it with several ruby versions: 2.2, 2.3, 2.4 and 2.5 just to make sure that it has nothing to do with it.
Further I tried the equivalent node version using the node sdk which also gives me a 500 on the figo side.

PS: Here is the full debug cruft:
โœ” 14:05:41 figo-ruby $ irb
irb(main):001:0> require 'figo'
=> true
irb(main):002:0> session = Figo::Session.new('ASHWLIkouP2O6_bgA2wWReRhletgWKHYjLqDaqb0LFfamim9RjexTo22ujRIP_cjLiRiSyQXyt2kM1eXU2XLFZQ0Hro15HikJQT_eNeT_9XQ')
=> #<Figo::Session:0x00007f90c09ad4b0 @access_token="ASHWLIkouP2O6_bgA2wWReRhletgWKHYjLqDaqb0LFfamim9RjexTo22ujRIP_cjLiRiSyQXyt2kM1eXU2XLFZQ0Hro15HikJQT_eNeT_9XQ", @https=#<Figo::HTTPS:0x00007f90c09ad438 @name="figo-ASHWLIkouP2O6_bgA2wWReRhletgWKHYjLqDaqb0LFfamim9RjexTo22ujRIP_cjLiRiSyQXyt2kM1eXU2XLFZQ0Hro15HikJQT_eNeT_9XQ", @debug_output=nil, @proxy_uri=nil, @no_proxy=[], @headers={}, @override_headers={}, @http_versions={}, @keep_alive=30, @open_timeout=nil, @read_timeout=nil, @idle_timeout=5, @max_requests=nil, @socket_options=[[6, 1, 1]], @ssl_generation=0, @pool=#<Net::HTTP::Persistent::Pool:0x00007f90c09ad1b8 @SiZe=2500, @timeout=5, @available=#<Net::HTTP::Persistent::TimedStackMulti:0x00007f90c09acda8 @create_block=#Proc:0x00007f90c09acd30@/Users/bijan/.rbenv/versions/2.5.0-dev/lib/ruby/gems/2.5.0/gems/net-http-persistent-3.0.0/lib/net/http/persistent.rb:525, @created=0, @que=[], @max=2500, @mutex=#Thread::Mutex:0x00007f90c09acc90, @resource=#Thread::ConditionVariable:0x00007f90c09acc68, @shutdown_block=nil, @Enqueued=0, @ques={}, @lru={}, @key=:"connection_args-70129841694420">, @key=:"current-70129841694420">, @certificate=nil, @ca_file="lib/cacert.pem", @ca_path=nil, @ciphers=nil, @private_key=nil, @ssl_timeout=nil, @ssl_version=nil, @verify_callback=#Proc:0x00007f90c09ac308@/Users/bijan/.rbenv/versions/2.5.0-dev/lib/ruby/gems/2.5.0/gems/figo-1.4.0/lib/helpers/https.rb:13, @verify_depth=nil, @verify_mode=1, @cert_store=nil, @generation=0, @reuse_ssl_sessions=true, @retry_change_requests=false>, @api_endpoint="api.figo.me">
irb(main):003:0> puts session.accounts.count
Figo::Error: We are very sorry, but something went wrong.
from /Users/bijan/.rbenv/versions/2.5.0-dev/lib/ruby/gems/2.5.0/gems/figo-1.4.0/lib/helpers/https.rb:48:in request' from /Users/bijan/.rbenv/versions/2.5.0-dev/lib/ruby/gems/2.5.0/gems/figo-1.4.0/lib/figo.rb:157:in query_api'
from /Users/bijan/.rbenv/versions/2.5.0-dev/lib/ruby/gems/2.5.0/gems/figo-1.4.0/lib/figo.rb:166:in query_api_object' from /Users/bijan/.rbenv/versions/2.5.0-dev/lib/ruby/gems/2.5.0/gems/figo-1.4.0/lib/account/api_call.rb:7:in accounts'
from (irb):3
from /Users/bijan/.rbenv/versions/2.5.0-dev/bin/irb:11:in `

'
irb(main):004:0>

Zombies HTTP connections raise TCP exception

Hello,
I've noticed that after a certain number of queries, the following exception occurs:
Failed to open TCP connection to api.figo.me:443 (getaddrinfo: Temporary failure in name resolution)

I've wrote and ran a test to get more insight in the problem. Here is the pseudo code for the test:

loop do
connection = Figo::Connection.new(...)
connection.credential_login(...) #Doesn't matter if the credentials are valid, this is just to send a query
end

On my machine, the loop breaks because of the TCP connection error after exactly 241 iterations.

After looking a the source code for Figo::Connection, I noticed that the HTTP connection was never shutdown. So I extended Figo::Connection like so:

class FigoConnection < Figo::Connection
  def done
    @https.shutdown
  end
end

and modified the test to call done after each query:

loop do
connection = FigoConnection.new(...)
connection.credential_login(...) 
connection.done
end

and now the loop keeps on going after 241 iterations.
The same issue exists in Figo::Session (I haven't tested it but the HTTP connection is managed the same way)

For now, I'm going to extend Figo::Connection and Figo::Session to handle the termination of HTTP connections in my application code.

It would be extremely nice if the ruby-figo gem would manage the connection itself, so this would be transparent to the application code.

Best regards.

Dependency not fixed

Figo assumes net-http-persistent (~> 2.9.4)
Now 3.0.0 was released and it breaks some calls for me because of interface changes in net-http-persistent major release.

Do not use global variables, use constants

In general it's a bad practise.

require 'figo'

$api_endpoint
 => "api.figo.me" 

$valid_fingerprints
 => ["3A:62:54:4D:86:B4:34:38:EA:34:64:4E:95:10:A9:FF:37:27:69:C0", "CF:C1:BC:7F:6A:16:09:2B:10:83:8A:B0:22:4F:3A:65:D2:70:D7:3E"]

Also the names are not prefixed, some potentially some other libary can introduce $api_endpoint variable, what can result into errors.

It's better to use constants:

module Figo
  API_ENDPOINT = "api.figo.me".freeze
  VALID_FINGERPRINTS = [
    "3A:62:54:4D:86:B4:34:38:EA:34:64:4E:95:10:A9:FF:37:27:69:C0",
    "CF:C1:BC:7F:6A:16:09:2B:10:83:8A:B0:22:4F:3A:65:D2:70:D7:3E"
  ].freeze
end

Session API outdated

Hi guys,

I just installed the latest 1.3.2 gem via bundler into my rails app. i'm using 4.2.7 on ruby 2.3.1.
Unfortunately, it seems that the underlying HTTP API changed and the Session constructor is not working anymore. At least the first demo call from your docs is not working at the moment.

this is my rails console output:

2.3.1 :001 > session = Figo::Session.new("ASHWLIkouP2O6_bgA2wWReRhletgWKHYjLqDaqb0LFfamim9RjexTo22ujRIP_cjLiRiSyQXyt2kM1eXU2XLFZQ0Hro15HikJQT_eNeT_9XQ")
ArgumentError: wrong number of arguments (given 2, expected 0)
	from /Users/magegu/.rvm/gems/ruby-2.3.1/gems/net-http-persistent-3.0.0/lib/net/http/persistent.rb:505:in `initialize'
	from /Users/magegu/.rvm/gems/ruby-2.3.1/gems/figo-1.3.2/lib/helpers/https.rb:9:in `initialize'
	from /Users/magegu/.rvm/gems/ruby-2.3.1/gems/figo-1.3.2/lib/figo.rb:123:in `new'
	from /Users/magegu/.rvm/gems/ruby-2.3.1/gems/figo-1.3.2/lib/figo.rb:123:in `initialize'
	from (irb):1:in `new'

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.