├── spec ├── data │ ├── foo │ └── theoffice.jpg ├── algorithmia_spec.rb ├── data_file_spec.rb ├── spec_helper.rb ├── data_dir_spec.rb └── algorithm_spec.rb ├── .rspec ├── lib ├── algorithmia │ ├── version.rb │ ├── unauthenticated_client.rb │ ├── data_object.rb │ ├── errors.rb │ ├── data_file.rb │ ├── response.rb │ ├── data_directory.rb │ ├── client.rb │ ├── requester.rb │ └── algorithm.rb └── algorithmia.rb ├── .travis.yml ├── Gemfile ├── bin ├── setup └── console ├── .gitignore ├── Rakefile ├── .gitlab-ci.yml ├── LICENSE ├── algorithmia.gemspec ├── CODE_OF_CONDUCT.md └── README.md /spec/data/foo: -------------------------------------------------------------------------------- 1 | bar -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | -------------------------------------------------------------------------------- /lib/algorithmia/version.rb: -------------------------------------------------------------------------------- 1 | module Algorithmia 2 | VERSION = "1.1.0" 3 | end 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.3.0 4 | before_install: gem install bundler -v 1.11.2 5 | -------------------------------------------------------------------------------- /spec/data/theoffice.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algorithmiaio/algorithmia-ruby/HEAD/spec/data/theoffice.jpg -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in algorithmia.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /lib/algorithmia/unauthenticated_client.rb: -------------------------------------------------------------------------------- 1 | module Algorithmia 2 | class UnauthenticatedClient < Client 3 | def initialize 4 | super(nil) 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | *.gem 11 | .DS_Store 12 | .ruby-version 13 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rspec/core/rake_task" 3 | 4 | # Default directory to look in is `/specs` 5 | # Run with `rake spec` 6 | RSpec::Core::RakeTask.new(:spec) 7 | 8 | task :default => :spec 9 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "algorithmia" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start 15 | -------------------------------------------------------------------------------- /spec/algorithmia_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Algorithmia do 4 | it 'has a version number' do 5 | expect(Algorithmia::VERSION).not_to be nil 6 | end 7 | end 8 | 9 | describe Algorithmia::Client do 10 | 11 | it 'sets the base url set to Algorithmia API endpoint' do 12 | expect(Algorithmia::Requester.base_uri).to end_with("algorithmia.com") 13 | end 14 | 15 | end 16 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: "ruby:2.6" 2 | 3 | # Cache gems in between builds 4 | cache: 5 | paths: 6 | - vendor/ruby 7 | 8 | before_script: 9 | - ruby -v # Print out ruby version for debugging 10 | - gem install bundler 11 | - gem install rspec 12 | - bundle install -j $(nproc) --path vendor # Install dependencies into ./vendor/ruby 13 | 14 | rspec: 15 | script: 16 | - rspec spec 17 | 18 | -------------------------------------------------------------------------------- /lib/algorithmia/data_object.rb: -------------------------------------------------------------------------------- 1 | module Algorithmia 2 | class DataObject 3 | attr_reader :data_uri 4 | 5 | def initialize(client, data_uri) 6 | @client = client 7 | @data_uri = data_uri 8 | sanitize_data_uri 9 | end 10 | 11 | def basename 12 | File.basename(@url) 13 | end 14 | 15 | def parent 16 | @client.dir(File.split(@data_uri).first) 17 | end 18 | 19 | private 20 | 21 | def sanitize_data_uri 22 | file_path = @data_uri.gsub('data://', '') 23 | @url = File.join('/v1/data/', file_path) 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/algorithmia/errors.rb: -------------------------------------------------------------------------------- 1 | module Algorithmia 2 | module Errors 3 | class Error < StandardError 4 | attr_reader :response 5 | 6 | def initialize(message, response) 7 | super(message) 8 | @response = response 9 | end 10 | end 11 | 12 | class AlgorithmError < Error; 13 | attr_reader :stacktrace 14 | def initialize(message, response, stacktrace) 15 | super(message, response) 16 | @stacktrace = stacktrace 17 | end 18 | end 19 | 20 | class ApiKeyEmptyError < Error; end 21 | class ApiKeyInvalidError < Error; end 22 | class InternalServerError < Error; end 23 | class JsonParseError < Error; end 24 | class NotFoundError < Error; end 25 | class UnauthorizedError < Error; end 26 | class ApiError < Error; end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/algorithmia.rb: -------------------------------------------------------------------------------- 1 | require_relative 'algorithmia/algorithm' 2 | require_relative 'algorithmia/client' 3 | require_relative 'algorithmia/unauthenticated_client' 4 | require_relative 'algorithmia/errors' 5 | require_relative 'algorithmia/requester' 6 | require_relative 'algorithmia/response' 7 | require_relative 'algorithmia/version' 8 | require_relative 'algorithmia/data_object' 9 | require_relative 'algorithmia/data_file' 10 | require_relative 'algorithmia/data_directory' 11 | 12 | module Algorithmia 13 | 14 | class << self 15 | def algo(endpoint) 16 | Algorithmia::UnauthenticatedClient.new.algo(endpoint) 17 | end 18 | 19 | def file(data_uri) 20 | Algorithmia::UnauthenticatedClient.new.file(data_uri) 21 | end 22 | 23 | def dir(data_uri) 24 | Algorithmia::UnauthenticatedClient.new.dir(data_uri) 25 | end 26 | 27 | def client(api_key=nil, base_url=nil) 28 | Algorithmia::Client.new(api_key, base_url) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/algorithmia/data_file.rb: -------------------------------------------------------------------------------- 1 | require 'tempfile' 2 | 3 | module Algorithmia 4 | class DataFile < DataObject 5 | 6 | def exists? 7 | Algorithmia::Requester.new(@client).head(@url) 8 | true 9 | rescue Errors::NotFoundError 10 | false 11 | end 12 | 13 | def get_file 14 | response = get 15 | 16 | tempfile = Tempfile.open(File.basename(@url)) do |f| 17 | f.write response 18 | f 19 | end 20 | 21 | File.new(tempfile.path) 22 | end 23 | 24 | def get 25 | Algorithmia::Requester.new(@client).get(@url).body 26 | end 27 | 28 | def put(data) 29 | Algorithmia::Requester.new(@client).put(@url, data, headers: {}) 30 | end 31 | 32 | alias_method :put_json, :put 33 | 34 | def put_file(file_path) 35 | data = File.binread(file_path) 36 | put(data) 37 | end 38 | 39 | def delete 40 | Algorithmia::Requester.new(@client).delete(@url) 41 | true 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Algorithmia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /algorithmia.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'algorithmia/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "algorithmia" 8 | spec.version = Algorithmia::VERSION 9 | spec.authors = ["Algorithmia"] 10 | spec.email = ["developer@algorithmia.com"] 11 | spec.licenses = ["MIT"] 12 | spec.summary = %q{A Ruby client for making API calls to Algorithmia} 13 | spec.description = %q{This gem provides a friendly client interface to the Algorithmia API, allowing you to call any of the algorithms on the Algorithmia Marketplace.} 14 | spec.homepage = "https://algorithmia.com/" 15 | 16 | spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 17 | spec.bindir = "exe" 18 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 19 | spec.require_paths = ["lib"] 20 | 21 | spec.add_development_dependency "bundler", "~> 2.0" 22 | spec.add_development_dependency "rake", "~> 11.1" 23 | spec.add_development_dependency "rspec", "~> 3.4" 24 | 25 | spec.add_dependency "json", "~> 2.0" 26 | spec.add_dependency "httparty", "~> 0.13" 27 | 28 | end 29 | -------------------------------------------------------------------------------- /lib/algorithmia/response.rb: -------------------------------------------------------------------------------- 1 | require "base64" 2 | 3 | module Algorithmia 4 | class Response 5 | attr_reader :response 6 | 7 | def initialize(response, output_type) 8 | @response = response 9 | @output_type = output_type 10 | end 11 | 12 | def result 13 | if @output_type == 'raw' 14 | @response 15 | elsif content_type == 'binary' 16 | Base64.decode64(@response["result"]) 17 | else 18 | @response["result"] 19 | end 20 | end 21 | 22 | def metadata 23 | if @output_type == 'raw' 24 | nil 25 | else 26 | @response["metadata"] 27 | end 28 | end 29 | 30 | def duration 31 | if @output_type == 'raw' 32 | nil 33 | else 34 | metadata["duration"] 35 | end 36 | end 37 | 38 | def content_type 39 | if @output_type == 'raw' 40 | nil 41 | else 42 | metadata["content_type"] 43 | end 44 | end 45 | 46 | def stdout 47 | if @output_type == 'raw' 48 | nil 49 | else 50 | metadata["stdout"] 51 | end 52 | end 53 | 54 | def alerts 55 | if @output_type == 'raw' 56 | nil 57 | else 58 | metadata["alerts"] 59 | end 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /spec/data_file_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'digest' 3 | 4 | describe Algorithmia::DataFile do 5 | 6 | def empty_dir 7 | dir = test_client.dir("data://.my/rubytest") 8 | dir.delete(true) if dir.exists? 9 | expect(dir.exists?).to be false 10 | dir.create 11 | expect(dir.exists?).to be true 12 | dir 13 | end 14 | 15 | it 'can resolve parent uri' do 16 | dir = Algorithmia.dir("data://.my/rubytest/foo") 17 | expect(dir.parent.data_uri).to eq("data://.my/rubytest") 18 | end 19 | 20 | it 'can read/write a text file' do 21 | dir = empty_dir 22 | file = dir.file("foo") 23 | 24 | expect(file.exists?).to be false 25 | file.put("string contents") 26 | expect(file.exists?).to be true 27 | 28 | expect(file.get).to eq("string contents") 29 | end 30 | 31 | it 'can read/write a binary file' do 32 | dir = empty_dir 33 | file = dir.file("foo") 34 | 35 | path = __dir__+'/data/theoffice.jpg' 36 | bytes = File.binread(path) 37 | expected_sha = Digest::SHA256.hexdigest(bytes) 38 | 39 | expect(file.exists?).to be false 40 | file.put(bytes) 41 | expect(file.exists?).to be true 42 | expect(Digest::SHA256.hexdigest(file.get)).to eq(expected_sha) 43 | end 44 | 45 | it "can delete a file" do 46 | dir = empty_dir 47 | file = dir.file("foo") 48 | 49 | expect(file.exists?).to be false 50 | file.put("string contents") 51 | expect(file.exists?).to be true 52 | file.delete 53 | expect(file.exists?).to be false 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'bundler/setup' 2 | Bundler.setup 3 | 4 | require_relative '../lib/algorithmia.rb' 5 | 6 | def test_client 7 | expect(ENV['ALGORITHMIA_API_KEY']).to_not be_nil 8 | Algorithmia.client(ENV['ALGORITHMIA_API_KEY']) 9 | end 10 | 11 | def test_admin 12 | expect(ENV['ALGORITHMIA_ADMIN_API_KEY']).to_not be_nil 13 | Algorithmia.client(ENV['ALGORITHMIA_ADMIN_API_KEY'], ENV['ALGORITHMIA_ADMIN_API']) 14 | end 15 | 16 | def create_test_algo 17 | algo_name = "CreateAlgoTest#{Time.now.to_i}" 18 | payload = {"name":algo_name,"details":{"label":"CreateAlgoTest"},"settings":{"environment":"cpu","language":"java","licence":"ap1","network_access":"full","pipeline_enabled":true,"source_visibility":"open"}}.to_json 19 | headers = { 20 | 'Content-Type' => 'application/json' 21 | } 22 | Algorithmia::Requester.new(test_client).post('/v1/algorithms/jakemuntarsi', payload, query: nil, headers: headers) 23 | algo_name 24 | end 25 | 26 | def create_test_org(org_name) 27 | timestamp = Time.now.to_i 28 | organization = OpenStruct.new(org_name: org_name, 29 | org_label: "myLabel", 30 | org_contact_name: "some owner", 31 | org_email: "#{org_name}_#{timestamp}@algo.com", 32 | org_url: "https://algorithmia.com", 33 | external_id: "ext_#{timestamp}", 34 | type_id: "basic", 35 | resource_type: "organization") 36 | test_admin.create_organization(organization.to_h.to_json) 37 | end 38 | 39 | def destroy_test_org (org_name) 40 | test_admin.delete_organization(org_name) 41 | end 42 | 43 | RSpec.configure do |config| 44 | config.color = true 45 | config.tty = true 46 | config.formatter = :documentation 47 | end 48 | -------------------------------------------------------------------------------- /spec/data_dir_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'digest' 3 | 4 | describe Algorithmia::DataDirectory do 5 | 6 | def nonexistent_dir 7 | dir = test_client.dir("data://.my/rubytest") 8 | dir.delete(true) if dir.exists? 9 | expect(dir.exists?).to be false 10 | dir 11 | end 12 | 13 | it 'can resolve parent uri' do 14 | dir = Algorithmia.dir("data://.my/rubytest") 15 | expect(dir.parent.data_uri).to eq("data://.my") 16 | end 17 | 18 | it 'can resolve child uri' do 19 | dir = Algorithmia.dir("data://.my/rubytest") 20 | expect(dir.file("foo").data_uri).to eq("data://.my/rubytest/foo") 21 | end 22 | 23 | it 'can create/delete a directory' do 24 | dir = nonexistent_dir 25 | 26 | expect(dir.create).to be true 27 | expect(dir.exists?).to be true 28 | dir.delete 29 | expect(dir.exists?).to be false 30 | end 31 | 32 | it 'can put a file' do 33 | dir = nonexistent_dir 34 | expect(dir.create).to be true 35 | 36 | dir.put_file(__dir__+'/data/foo') 37 | file = dir.file('foo') 38 | expect(file.exists?).to be true 39 | end 40 | 41 | it 'does not delete dir with files' do 42 | dir = nonexistent_dir 43 | expect(dir.create).to be true 44 | 45 | dir.put_file(__dir__+'/data/foo') 46 | expect{dir.delete}.to raise_error(Algorithmia::Errors::ApiError) 47 | expect(dir.exists?).to be true 48 | end 49 | 50 | it 'can force delete dir with files' do 51 | dir = nonexistent_dir 52 | expect(dir.create).to be true 53 | 54 | dir.put_file(__dir__+'/data/foo') 55 | dir.delete(true) 56 | expect(dir.exists?).to be false 57 | end 58 | 59 | it 'can list directory' do 60 | dir = nonexistent_dir 61 | expect(dir.create).to be true 62 | 63 | files = dir.each_file.map { |f| f.data_uri } 64 | expect(files.length).to eq(0) 65 | 66 | dir.file("foo").put("testdata") 67 | files = dir.each_file.map { |f| f.data_uri } 68 | expect(files.length).to eq(1) 69 | 70 | dir.file("bar").put("testdata") 71 | files = dir.each_file.map { |f| f.data_uri } 72 | expect(files.length).to eq(2) 73 | dir.each_file { |f| 74 | expect(f.get).to eq("testdata") 75 | } 76 | end 77 | 78 | end 79 | -------------------------------------------------------------------------------- /lib/algorithmia/data_directory.rb: -------------------------------------------------------------------------------- 1 | require 'uri' 2 | 3 | module Algorithmia 4 | class DataDirectory < DataObject 5 | 6 | def exists? 7 | Algorithmia::Requester.new(@client).get(@url) 8 | true 9 | rescue Errors::NotFoundError 10 | false 11 | end 12 | 13 | def create 14 | parent, name = File.split(@url) 15 | Algorithmia::Requester.new(@client).post(parent, name: name) 16 | true 17 | end 18 | 19 | def delete(force = false) 20 | query = {} 21 | query[:force] = true if force 22 | Algorithmia::Requester.new(@client).delete(@url, query: query) 23 | true 24 | end 25 | 26 | def each(&block) 27 | return enum_for(:each) unless block_given? 28 | 29 | list(block) do |dir| 30 | extract_files(dir) + extract_folders(dir) 31 | end 32 | end 33 | 34 | def each_file(&block) 35 | return enum_for(:each_file) unless block_given? 36 | 37 | list(block) do |dir| 38 | extract_files(dir) 39 | end 40 | end 41 | 42 | def each_dir(&block) 43 | return enum_for(:each_dir) unless block_given? 44 | 45 | list(block) do |dir| 46 | extract_folders(dir) 47 | end 48 | end 49 | 50 | def file(file_name) 51 | @client.file(URI.encode(File.join(@data_uri, file_name))) 52 | end 53 | 54 | def put_file(file_path) 55 | file(File.basename(file_path)).put_file(file_path) 56 | end 57 | 58 | private 59 | 60 | def extract_files(dir) 61 | files = dir.parsed_response['files'] || [] 62 | files.map do |f| 63 | file(f['filename']) 64 | end 65 | end 66 | 67 | def extract_folders(dir) 68 | folders = dir.parsed_response['folders'] || [] 69 | folders.map do |f| 70 | @client.dir(URI.encode(File.join(@data_uri, f['name']))) 71 | end 72 | end 73 | 74 | def list(each_proc) 75 | marker = nil 76 | 77 | loop do 78 | query = {} 79 | query[:marker] = marker if marker 80 | 81 | dir = Algorithmia::Requester.new(@client).get(@url, query: query) 82 | 83 | items = yield dir 84 | items.each(&each_proc) 85 | 86 | marker = dir.parsed_response['marker'] 87 | break unless marker && !marker.empty? 88 | end 89 | end 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, and in the interest of 4 | fostering an open and welcoming community, we pledge to respect all people who 5 | contribute through reporting issues, posting feature requests, updating 6 | documentation, submitting pull requests or patches, and other activities. 7 | 8 | We are committed to making participation in this project a harassment-free 9 | experience for everyone, regardless of level of experience, gender, gender 10 | identity and expression, sexual orientation, disability, personal appearance, 11 | body size, race, ethnicity, age, religion, or nationality. 12 | 13 | Examples of unacceptable behavior by participants include: 14 | 15 | * The use of sexualized language or imagery 16 | * Personal attacks 17 | * Trolling or insulting/derogatory comments 18 | * Public or private harassment 19 | * Publishing other's private information, such as physical or electronic 20 | addresses, without explicit permission 21 | * Other unethical or unprofessional conduct 22 | 23 | Project maintainers have the right and responsibility to remove, edit, or 24 | reject comments, commits, code, wiki edits, issues, and other contributions 25 | that are not aligned to this Code of Conduct, or to ban temporarily or 26 | permanently any contributor for other behaviors that they deem inappropriate, 27 | threatening, offensive, or harmful. 28 | 29 | By adopting this Code of Conduct, project maintainers commit themselves to 30 | fairly and consistently applying these principles to every aspect of managing 31 | this project. Project maintainers who do not follow or enforce the Code of 32 | Conduct may be permanently removed from the project team. 33 | 34 | This code of conduct applies both within project spaces and in public spaces 35 | when an individual is representing the project or its community. 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 38 | reported by contacting a project maintainer at support@algorithmia.com. All 39 | complaints will be reviewed and investigated and will result in a response that 40 | is deemed necessary and appropriate to the circumstances. Maintainers are 41 | obligated to maintain confidentiality with regard to the reporter of an 42 | incident. 43 | 44 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 45 | version 1.3.0, available at 46 | [http://contributor-covenant.org/version/1/3/0/][version] 47 | 48 | [homepage]: http://contributor-covenant.org 49 | [version]: http://contributor-covenant.org/version/1/3/0/ -------------------------------------------------------------------------------- /lib/algorithmia/client.rb: -------------------------------------------------------------------------------- 1 | module Algorithmia 2 | class Client 3 | attr_reader :api_key 4 | attr_reader :api_address 5 | 6 | def initialize(api_key, api_address=nil) 7 | @api_key = api_key 8 | @api_address = api_address || ENV['ALGORITHMIA_API'] || "https://api.algorithmia.com" 9 | end 10 | 11 | def algo(endpoint, path = '/v1/algo/') 12 | Algorithmia::Algorithm.new(self, endpoint.prepend(path)) 13 | end 14 | 15 | def get_algo(user_name, algo_name) 16 | algo(user_name.concat('/').concat(algo_name), '/v1/algorithms/').get_algo 17 | end 18 | 19 | def get_algo_versions(user_name, algo_name, callable, limit, published, marker) 20 | algo(user_name.concat('/').concat(algo_name).concat('/versions'), '/v1/algorithms/') 21 | .algo_versions(callable, limit, published, marker) 22 | end 23 | 24 | def get_algo_builds(user_name, algo_name, limit, marker) 25 | algo(user_name.concat('/').concat(algo_name).concat('/builds'), '/v1/algorithms/') 26 | .algo_builds(limit, marker) 27 | end 28 | 29 | def list_scms() 30 | algo('', '/v1/scms').list_scms 31 | end 32 | 33 | def get_scm(scm_id) 34 | algo('/'.concat(scm_id), '/v1/scms').get_scm 35 | end 36 | 37 | def get_scm_status(user_name, algo_name) 38 | algo(user_name.concat('/').concat(algo_name).concat('/scm/status'), '/v1/algorithms/').get_scm_status 39 | end 40 | 41 | def get_algo_build_logs(user_name, algo_name, build_id) 42 | algo(user_name.concat('/').concat(algo_name) 43 | .concat('/builds/').concat(build_id) 44 | .concat('/logs'), '/v1/algorithms/') 45 | .algo_build_logs 46 | end 47 | 48 | def revoke_scm_status(scm_id) 49 | algo(scm_id.concat('/oauth/revoke'), '/v1/scms/').revoke_scm_status 50 | end 51 | 52 | def delete_algo(user_name, algo_name) 53 | algo(user_name.concat('/').concat(algo_name), '/v1/algorithms/').delete_algo 54 | end 55 | 56 | def create_organization(organization) 57 | org = JSON.parse(organization) 58 | org["type_id"] = get_organization_type_id(org["type_id"]) 59 | algo('/organizations', '/v1').create_organization(org.to_json) 60 | end 61 | 62 | def update_organization(org_name, organization) 63 | org = JSON.parse(organization) 64 | org["type_id"] = get_organization_type_id(org["type_id"]) 65 | algo("/organizations/#{org_name}", '/v1').update_organization(org.to_json) 66 | end 67 | 68 | def get_organization(org_name) 69 | algo("/organizations/#{org_name}", '/v1').get_organization 70 | end 71 | 72 | def get_organization_type_id(type) 73 | algo("/organization/types", '/v1').get_organization_type_id(type) 74 | end 75 | 76 | def delete_organization(org_name) 77 | algo("/organizations/#{org_name}", '/v1').delete_organization 78 | end 79 | 80 | def file(endpoint) 81 | Algorithmia::DataFile.new(self, endpoint) 82 | end 83 | 84 | def dir(endpoint) 85 | Algorithmia::DataDirectory.new(self, endpoint) 86 | end 87 | end 88 | end 89 | -------------------------------------------------------------------------------- /lib/algorithmia/requester.rb: -------------------------------------------------------------------------------- 1 | require 'httparty' 2 | 3 | module Algorithmia 4 | class Requester 5 | include HTTParty 6 | 7 | def initialize(client) 8 | self.class.base_uri client.api_address 9 | @client = client 10 | @default_headers = { 11 | 'User-Agent' => 'Algorithmia Ruby Client' 12 | } 13 | unless @client.api_key.nil? 14 | @default_headers['Authorization'] = @client.api_key 15 | end 16 | end 17 | 18 | def get(endpoint, query: {}, headers: {}) 19 | headers = merge_headers(headers) 20 | headers.delete('Content-Type') # No content, can break request parsing 21 | response = self.class.get(endpoint, query: query, headers: headers) 22 | check_for_errors(response) 23 | response 24 | end 25 | 26 | def post(endpoint, body, query: {}, headers: {}, timeout: 60) 27 | headers = merge_headers(headers) 28 | 29 | headers['Content-Type'] ||= case 30 | when body.kind_of?(String) && body.encoding == Encoding::ASCII_8BIT 31 | 'application/octet-stream' 32 | when body.kind_of?(String) 33 | 'text/plain' 34 | else 35 | body = body.to_json 36 | 'application/json' 37 | end 38 | 39 | response = self.class.post(endpoint, body: body, query: query, headers: headers, timeout: timeout) 40 | check_for_errors(response) 41 | response 42 | end 43 | 44 | def put(endpoint, body, query: {}, headers: {}) 45 | headers = merge_headers(headers) 46 | 47 | headers['Content-Type'] ||= case 48 | when body.kind_of?(String) && body.encoding == Encoding::ASCII_8BIT 49 | 'application/octet-stream' 50 | when body.kind_of?(String) 51 | 'text/plain' 52 | else 53 | body = body.to_json 54 | 'application/json' 55 | end 56 | 57 | response = self.class.put(endpoint, body: body, query: query, headers: headers) 58 | check_for_errors(response) 59 | response 60 | end 61 | 62 | def head(endpoint) 63 | headers = merge_headers({}) 64 | headers.delete('Content-Type') # No content, can break request parsing 65 | response = self.class.head(endpoint, headers: headers) 66 | check_for_errors(response) 67 | response 68 | end 69 | 70 | def delete(endpoint, query: {}) 71 | response = self.class.delete(endpoint, query: query, headers: @default_headers) 72 | check_for_errors(response) 73 | response 74 | end 75 | 76 | private 77 | 78 | def check_for_errors(response) 79 | if response.code >= 200 && response.code < 300 80 | if response.respond_to?(:has_key?) and response['error'] 81 | error = response['error'] 82 | raise Errors::AlgorithmError.new(error["message"], response, error["stacktrace"]) 83 | end 84 | return 85 | end 86 | 87 | message = response.dig("error", "message") if response.is_a?(Hash) 88 | 89 | case response.code 90 | when 401 91 | message ||= "The request you are making requires authorization. Please check that you have permissions & that you've set your API key." 92 | raise Errors::UnauthorizedError.new(message, response) 93 | when 404 94 | message ||= "The URI requested is invalid or the resource requested does not exist." 95 | raise Errors::NotFoundError.new(message, response) 96 | when 500..599 97 | message ||= "There was a problem communicating with Algorithmia. Please contact Algorithmia Support." 98 | raise Errors::InternalServerError.new(message, response) 99 | else 100 | message ||= "#{response.code} - an unknown error occurred" 101 | raise Errors::ApiError.new(message, response) 102 | end 103 | end 104 | 105 | def merge_headers(headers = {}) 106 | @default_headers.merge(headers) 107 | end 108 | end 109 | end 110 | -------------------------------------------------------------------------------- /spec/algorithm_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'digest' 3 | require 'json' 4 | 5 | describe Algorithmia::Algorithm do 6 | it 'can make json api call' do 7 | input = ["transformer", "terraforms", "retransform"] 8 | response = test_client.algo("WebPredict/ListAnagrams/0.1.0").pipe(input) 9 | expect(response.content_type).to eq('json') 10 | expect(response.result).to eq(["transformer","retransform"]) 11 | end 12 | 13 | it 'can make raw json api call' do 14 | input = ["transformer", "terraforms", "retransform"].to_json 15 | response = test_client.algo("WebPredict/ListAnagrams/0.1.0").pipe_json(input) 16 | expect(response.content_type).to eq('json') 17 | expect(response.result).to eq(["transformer","retransform"]) 18 | end 19 | 20 | it 'can make text api call' do 21 | input = "foo" 22 | response = test_client.algo("demo/hello").pipe(input) 23 | expect(response.content_type).to eq('text') 24 | expect(response.result).to eq("Hello foo") 25 | end 26 | 27 | it 'can make binary api call' do 28 | image_in = __dir__+'/data/theoffice.jpg' 29 | input = File.binread(image_in) 30 | response = test_client.algo("opencv/SmartThumbnail/0.1.14").pipe(input) 31 | 32 | expected_sha = "1e8042669864e34a65ba05b1c457e24aab7184dd5990c9377791e37890ac8760" 33 | expect(response.content_type).to eq('binary') 34 | expect(response.result.encoding).to eq(Encoding::ASCII_8BIT) 35 | expect(Digest::SHA256.hexdigest(response.result)).to eq(expected_sha) 36 | end 37 | 38 | # it 'can set timeout' do 39 | # algo = test_client.algo("kenny/sleep").set(timeout: 1) 40 | # expect{ algo.pipe(2) }.to raise_error(Algorithmia::Errors::AlgorithmError) 41 | # end 42 | 43 | it 'can get an Algorithm object from this client' do 44 | result = test_client.get_algo("jakemuntarsi", "Hello") 45 | expect(result.response["name"]).to eq('Hello') 46 | expect(result.response["resource_type"]).to eq('algorithm') 47 | end 48 | 49 | it 'it list algorithm versions from this client' do 50 | result = test_client.get_algo_versions("jakemuntarsi", "Hello", nil, nil, nil, nil) 51 | expect(result.instance_variable_get(:@response)['results'].size).to eq(2) 52 | expect(result.instance_variable_get(:@response)['results'][1]["name"]).to eq('Hello') 53 | end 54 | 55 | it 'it list algorithm builds from this client' do 56 | result = test_client.get_algo_builds("jakemuntarsi", "Hello", nil, nil) 57 | expect(result.instance_variable_get(:@response)['results'].size).to eq(2) 58 | end 59 | 60 | it 'it Get build logs for an Algorithm object from this client' do 61 | result = test_client.get_algo_build_logs("jakemuntarsi", "Hello", '2fb99aaa-9634-487f-b6bd-22d55c183b43') 62 | expect(result.instance_variable_get(:@response)['logs']).to be_truthy 63 | end 64 | 65 | it 'it Delete an Algorithm from this client' do 66 | algo_name = create_test_algo 67 | result = test_client.delete_algo("jakemuntarsi", algo_name) 68 | expect(result.instance_variable_get(:@response)).to be_nil 69 | end 70 | 71 | it 'it Get Algorithm SCM Status from this client' do 72 | result = test_client.get_scm_status("jakemuntarsi", "Hello") 73 | expect(result.response["scm_connection_status"]).to eq('active') 74 | end 75 | 76 | it 'it Creates an Organization from this client' do 77 | time_now = Time.now.to_i 78 | org_name = "MyOrg#{time_now}" 79 | organization = OpenStruct.new(org_name: org_name, 80 | org_label: "myLabel", 81 | org_contact_name: "some owner", 82 | org_email: "#{org_name}@algo.com", 83 | org_url: "https://#{org_name}algo.com", 84 | external_id: "ext_#{time_now}", 85 | type_id: "basic") 86 | 87 | result = test_admin.create_organization(organization.to_h.to_json) 88 | expect(result.response["org_name"]).to eq(org_name) 89 | expect(result.response["org_label"]).to eq("myLabel") 90 | expect(result.response["org_contact_name"]).to eq("some owner") 91 | expect(result.response["org_email"]).to eq("#{org_name}@algo.com") 92 | expect(result.response["org_url"]).to eq("https://#{org_name}algo.com") 93 | expect(result.response["external_id"]).to eq("ext_#{time_now}") 94 | destroy_test_org org_name 95 | end 96 | 97 | it 'it Get an Organization from this client' do 98 | org_name = "MyOrg#{Time.now.to_i}" 99 | create_test_org org_name 100 | sleep(5) 101 | result = test_admin.get_organization org_name 102 | expect(result.response["org_name"]).to eq(org_name) 103 | destroy_test_org org_name 104 | end 105 | 106 | it 'it Updates an Organization from this client' do 107 | time_now = Time.now.to_i 108 | org_name = "MyOrg#{time_now}" 109 | organization = create_test_org(org_name).response 110 | organization["org_label"] = "MyOrgLabel#{time_now}" 111 | organization["type_id"] = "legacy" 112 | organization["org_contact_name"] = "NewContactName#{time_now}" 113 | test_admin.update_organization(org_name, organization.to_json) 114 | sleep(5) 115 | result = test_admin.get_organization org_name 116 | expect(result.response["org_name"]).to eq(org_name) 117 | expect(result.response["org_label"]).to eq("MyOrgLabel#{time_now}") 118 | expect(result.response["org_contact_name"]).to eq("NewContactName#{time_now}") 119 | destroy_test_org org_name 120 | end 121 | 122 | #it 'it Revoke an Algorithm SCM status from this client' do 123 | # result = test_client.revoke_scm_status( "internal") 124 | # expect(result.response["scm_connection_status"]).to eq('active') 125 | #end 126 | 127 | end 128 | -------------------------------------------------------------------------------- /lib/algorithmia/algorithm.rb: -------------------------------------------------------------------------------- 1 | module Algorithmia 2 | class Algorithm 3 | 4 | def initialize(client, endpoint) 5 | @client = client 6 | @endpoint = endpoint 7 | @query = {} 8 | end 9 | 10 | def set(options_hash) 11 | @query.update(options_hash) 12 | self 13 | end 14 | 15 | def pipe(input) 16 | client_timeout = (@query[:timeout] || 300) + 10 17 | response = Algorithmia::Requester.new(@client).post(@endpoint, input, query: @query, headers: {}, timeout: client_timeout) 18 | Algorithmia::Response.new(response.parsed_response, @query[:output]) 19 | end 20 | 21 | def pipe_json(input) 22 | client_timeout = (@query[:timeout] || 300) + 10 23 | headers = { 24 | 'Content-Type' => 'application/json' 25 | } 26 | response = Algorithmia::Requester.new(@client).post(@endpoint, input, query: @query, headers: headers, timeout: client_timeout) 27 | Algorithmia::Response.new(response.parsed_response, @query[:output]) 28 | end 29 | 30 | def get_algo 31 | headers = { 32 | 'Content-Type' => 'application/json' 33 | } 34 | response = Algorithmia::Requester.new(@client).get(@endpoint, query: @query, headers: headers) 35 | Algorithmia::Response.new(response.parsed_response, @query[:output]) 36 | end 37 | 38 | def delete_algo 39 | response = Algorithmia::Requester.new(@client).delete(@endpoint, query: @query) 40 | Algorithmia::Response.new(response.parsed_response, @query[:output]) 41 | end 42 | 43 | def algo_versions(callable, limit, published, marker) 44 | headers = { 45 | 'Content-Type' => 'application/json' 46 | } 47 | @query[:callable] = callable if callable 48 | @query[:limit] = limit if limit 49 | @query[published] = published if published 50 | @query[marker] = marker if marker 51 | response = Algorithmia::Requester.new(@client).get(@endpoint, query: @query, headers: headers) 52 | Algorithmia::Response.new(response.parsed_response, @query[:output]) 53 | end 54 | 55 | def algo_builds(limit, marker) 56 | headers = { 57 | 'Content-Type' => 'application/json' 58 | } 59 | @query[:limit] = limit if limit 60 | @query[marker] = marker if marker 61 | response = Algorithmia::Requester.new(@client).get(@endpoint, query: @query, headers: headers) 62 | Algorithmia::Response.new(response.parsed_response, @query[:output]) 63 | end 64 | 65 | def algo_build_logs() 66 | headers = { 67 | 'Content-Type' => 'application/json' 68 | } 69 | response = Algorithmia::Requester.new(@client).get(@endpoint, query: @query, headers: headers) 70 | Algorithmia::Response.new(response.parsed_response, @query[:output]) 71 | end 72 | 73 | def list_scms() 74 | headers = { 75 | 'Content-Type' => 'application/json' 76 | } 77 | response = Algorithmia::Requester.new(@client).get(@endpoint, query: @query, headers: headers) 78 | Algorithmia::Response.new(response.parsed_response, @query[:output]) 79 | end 80 | 81 | def get_scm() 82 | headers = { 83 | 'Content-Type' => 'application/json' 84 | } 85 | response = Algorithmia::Requester.new(@client).get(@endpoint, query: @query, headers: headers) 86 | Algorithmia::Response.new(response.parsed_response, @query[:output]) 87 | end 88 | 89 | def get_scm_status 90 | headers = { 91 | 'Content-Type' => 'application/json' 92 | } 93 | response = Algorithmia::Requester.new(@client).get(@endpoint, query: @query, headers: headers) 94 | Algorithmia::Response.new(response.parsed_response, @query[:output]) 95 | end 96 | 97 | def revoke_scm_status() 98 | headers = { 99 | 'Content-Type' => 'application/json' 100 | } 101 | response = Algorithmia::Requester.new(@client).get(@endpoint, query: @query, headers: headers) 102 | Algorithmia::Response.new(response.parsed_response, @query[:output]) 103 | end 104 | 105 | def create_organization(input) 106 | client_timeout = (@query[:timeout] || 300) + 10 107 | headers = { 108 | 'Content-Type' => 'application/json' 109 | } 110 | response = Algorithmia::Requester.new(@client).post(@endpoint, input, query: @query, headers: headers, timeout: client_timeout) 111 | Algorithmia::Response.new(response.parsed_response, @query[:output]) 112 | end 113 | 114 | def update_organization(input) 115 | headers = { 116 | 'Content-Type' => 'application/json' 117 | } 118 | response = Algorithmia::Requester.new(@client).put(@endpoint, input, query: @query, headers: headers) 119 | Algorithmia::Response.new(response.parsed_response, @query[:output]) 120 | end 121 | 122 | def get_organization 123 | headers = { 124 | 'Content-Type' => 'application/json' 125 | } 126 | response = Algorithmia::Requester.new(@client).get(@endpoint, query: @query, headers: headers) 127 | Algorithmia::Response.new(response.parsed_response, @query[:output]) 128 | end 129 | 130 | def delete_organization 131 | response = Algorithmia::Requester.new(@client).delete(@endpoint, query: @query) 132 | Algorithmia::Response.new(response.parsed_response, @query[:output]) 133 | end 134 | 135 | def get_organization_type_id(type) 136 | headers = { 137 | 'Content-Type' => 'application/json' 138 | } 139 | response = Algorithmia::Requester.new(@client).get(@endpoint, query: @query, headers: headers) 140 | response.parsed_response.filter{ |r| r["name"] == type }.first["id"] 141 | end 142 | 143 | 144 | 145 | end 146 | end 147 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Algorithmia Ruby Client 2 | 3 | The Algorithmia Ruby client is a wrapper for making calls to the Algorithmia API and Data API. 4 | 5 | With Algorithmia, you can leverage algorithms written in any language by including them in your Ruby project with a simple API call! Browse the collection of algorithms available on [Algorithmia.com](http://algorithmia.com). 6 | 7 | ## Getting started 8 | 9 | The Algorithmia ruby client is [available on rubygems](https://rubygems.org/gems/algorithmia). 10 | Simply add `gem 'algorithmia'` to your application's Gemfile and run `bundle install`. 11 | 12 | Then create an Algorithmia client and authenticate with your API key: 13 | 14 | ```ruby 15 | require 'algorithmia' 16 | 17 | client = Algorithmia.client('YOUR_API_KEY') 18 | ``` 19 | 20 | You are now ready to call algorithms. 21 | 22 | ## Calling algorithms 23 | 24 | The following examples of calling algorithms are organized by type of input/output which vary between algorithms. 25 | 26 | Note: a single algorithm may have different input and output types, or accept multiple types of input, 27 | so consult the algorithm's description for usage examples specific to that algorithm. 28 | 29 | ### Text input/output 30 | 31 | Call an algorithm with text input by simply passing a string into its `pipe` method. 32 | If the algorithm output is text, then the result field of the response will be a string. 33 | 34 | ```ruby 35 | algo = client.algo('demo/Hello/0.1.1') 36 | puts algo.pipe('HAL 9000').result 37 | # -> Hello HAL 900 38 | ``` 39 | 40 | ### JSON input/output 41 | 42 | Call an algorithm with JSON input by simply passing in a type that can be serialized to JSON, like an `Array` or `Hash`. 43 | For algorithms that return JSON, the result field of the response will be the appropriate deserialized type. 44 | 45 | ```ruby 46 | algo = client.algo('WebPredict/ListAnagrams/0.1.0') 47 | result = algo.pipe(["transformer", "terraforms", "retransform"]).result 48 | # -> ["transformer","retransform"] 49 | ``` 50 | 51 | Alternatively, if your input is already serialized to JSON, you may call `pipe_json`: 52 | 53 | ```ruby 54 | algo = client.algo('WebPredict/ListAnagrams/0.1.0') 55 | result = algo.pipe_json('["transformer", "terraforms", "retransform"]').result 56 | # -> ["transformer","retransform"] 57 | ``` 58 | 59 | ### Binary input/output 60 | 61 | Call an algorithm with Binary input by passing an `ASCII-8BIT`-encoded string into the `pipe` method. 62 | Similarly, if the algorithm response is binary data, then the result field of the response will be an `ASCII-8BIT` string. 63 | In practice, this involves working with methods like `IO.binread` and `IO.binwrite`. 64 | 65 | ```ruby 66 | input = File.binread("/path/to/bender.png") 67 | result = client.algo("opencv/SmartThumbnail/0.1").pipe(input).result 68 | # -> [ASCII-8BIT string of binary data] 69 | ``` 70 | 71 | ### Error handling 72 | 73 | API errors and Algorithm exceptions will be raised when calling `pipe`: 74 | 75 | ```ruby 76 | client.algo('util/whoopsWrongAlgo').pipe('Hello, world!') 77 | # -> Algorithmia::Errors::NotFoundError: algorithm algo://util/whoopsWrongAlgo not found 78 | ``` 79 | 80 | And you can rescue Algorithm errors separate from other errors: 81 | 82 | ```ruby 83 | begin 84 | client.algo('demo/Hello').pipe('world!') 85 | rescue Algorithmia::Errors::AlgorithmError => e 86 | puts "Algorithm Error: #{e.message}" 87 | puts e.stacktrace 88 | rescue => e 89 | puts "Error: #{e.message}" 90 | end 91 | 92 | ``` 93 | 94 | ### Request options 95 | 96 | The client exposes options that can configure algorithm requests. 97 | This includes support for changing the timeout or indicating that the API should include stdout in the response. 98 | 99 | ```ruby 100 | algo = client.algo('demo/Hello/0.1.1').set(timeout: 10, stdout: true) 101 | response = algo.pipe('HAL 9000') 102 | stdout = response.stdout 103 | ``` 104 | 105 | Note: `stdout: true` is ignored if you do not have access to the algorithm source. 106 | 107 | 108 | ## Working with data 109 | 110 | The Algorithmia Java client also provides a way to manage both Algorithmia hosted data 111 | and data from Dropbox or S3 accounts that you've connected to you Algorithmia account. 112 | 113 | This client provides a `DataFile` type (generally created by `client.file(uri)`) 114 | and a `DataDir` type (generally created by `client.dir(uri)`) that provide methods for managing your data. 115 | 116 | ### Create directories 117 | 118 | Create directories by instantiating a `DataDirectory` object and calling `create`: 119 | 120 | ```ruby 121 | client.dir("data://.my/robots").create 122 | client.dir("dropbox://robots").create 123 | ``` 124 | 125 | ### Upload files to a directory 126 | 127 | Upload files by calling `put` on a `DataFile` object, or by calling `putFile` on a `DataDirectory` object. 128 | 129 | ```ruby 130 | robots = client.dir("data://.my/robots") 131 | 132 | # Upload local file 133 | robots.put_file("/path/to/Optimus_Prime.png") 134 | # Write a text file 135 | robots.file("Optimus_Prime.txt").put("Leader of the Autobots") 136 | # Write a binary file 137 | robots.file("Optimus_Prime.key").put([71, 101, 101, 107].pack('C*')) 138 | ``` 139 | 140 | ### Download contents of file 141 | 142 | Download files by calling `get` or `get_file` on a `DataFile` object: 143 | 144 | ```ruby 145 | # Download file and get the file handle 146 | t800File = client.file("data://.my/robots/T-800.png").get_file 147 | 148 | # Get file's contents as a string 149 | t800Text = client.file("data://.my/robots/T-800.txt").get 150 | 151 | # Get file's contents as JSON 152 | t800JsonString = client.file("data://.my/robots/T-800.txt").get 153 | t800Json = JSON.parse(t800JsonString) 154 | 155 | # Get file's contents as a byte array 156 | t800Bytes = client.file("data://.my/robots/T-800.png").get 157 | ``` 158 | 159 | ### Delete files and directories 160 | 161 | Delete files and directories by calling delete on their respective `DataFile` or `DataDirectory` object. 162 | DataDirectories take an optional `force` parameter that indicates whether the directory should be deleted 163 | if it contains files or other directories. 164 | 165 | ```ruby 166 | client.file("data://.my/robots/C-3PO.txt").delete 167 | 168 | client.dir("data://.my/robots").delete(false) 169 | ``` 170 | 171 | ### List directory contents 172 | 173 | Iterate over the contents of a directory using the iterated returned 174 | by calling `each`, `each_directory`, or `each_file` on a `DataDirectory` object. 175 | If no block is given to the method, an enumerator will be returned. 176 | 177 | ```ruby 178 | # List top level directories 179 | client.dir("data://.my").each_dir do |dir| 180 | puts "Directory " + dir.data_uri 181 | end 182 | 183 | # List files in the 'robots' directory 184 | client.dir("data://.my/robots").each_file do |file| 185 | puts "File " + file.data_uri 186 | end 187 | 188 | # Iterate all directory contents 189 | client.dir("dropbox://").each do |item| 190 | puts file.data_uri 191 | end 192 | ``` 193 | 194 | ## Development 195 | 196 | After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. 197 | 198 | To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). 199 | 200 | ## Contributing 201 | 202 | Bug reports and pull requests are welcome on [GitHub](https://github.com/algorithmiaio/algorithmia-ruby). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. 203 | 204 | --------------------------------------------------------------------------------