spacialdb 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md ADDED
@@ -0,0 +1,23 @@
1
+ SpacialDB API - provision geospatial databases on SpacialDB from the command line
2
+ =================================================================================
3
+
4
+ This library wraps the SpacialDB's REST API for provisioning and managing geospatial databases in SpacialDB's cloud service.
5
+
6
+ Sample Workflow
7
+ ---------------
8
+
9
+ Signup and create a new database:
10
+
11
+ spacialdb signup
12
+ spacialdb create
13
+ spacialdb list
14
+
15
+ Delete a database:
16
+
17
+ spacialdb destroy --db <db name>
18
+
19
+ Setup
20
+ -----
21
+
22
+ gem install spacialdb
23
+
data/bin/spacialdb ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
5
+
6
+ require 'spacialdb'
7
+ require 'spacialdb/command'
8
+
9
+ args = ARGV.dup
10
+ ARGV.clear
11
+ command = args.shift.strip rescue 'help'
12
+
13
+ Spacialdb::Command.load
14
+ Spacialdb::Command.run(command, args)
@@ -0,0 +1,189 @@
1
+ require "spacialdb/client"
2
+ require "spacialdb/helpers"
3
+
4
+ class Spacialdb::Auth
5
+ class PasswordMismatch < RuntimeError; end
6
+
7
+ class << self
8
+ attr_accessor :credentials
9
+
10
+ def default_host
11
+ "beta.spacialdb.com"
12
+ end
13
+
14
+ def host
15
+ ENV['SPACIALDB_HOST'] || default_host
16
+ end
17
+
18
+ def client
19
+ @client ||= begin
20
+ client = Spacialdb::Client.new(email, private_api_key, host)
21
+ client
22
+ end
23
+ end
24
+
25
+ def signup
26
+ delete_credentials
27
+ signup_and_save_credentials
28
+ display "Signed up successfully."
29
+ end
30
+
31
+ def login
32
+ delete_credentials
33
+ get_credentials
34
+ display "Logged in successfully."
35
+ end
36
+
37
+ def logout
38
+ delete_credentials
39
+ display "Logged out successfully."
40
+ end
41
+
42
+ def clear
43
+ @credentials = nil
44
+ @client = nil
45
+ end
46
+
47
+ def email
48
+ get_credentials
49
+ @credentials[0]
50
+ end
51
+
52
+ def private_api_key
53
+ get_credentials
54
+ @credentials[1]
55
+ end
56
+
57
+ def get_credentials
58
+ return if @credentials
59
+ unless @credentials = read_credentials
60
+ ask_for_and_save_credentials
61
+ end
62
+ @credentials
63
+ end
64
+
65
+ def read_credentials
66
+ File.exists?(credentials_file) and File.read(credentials_file).split("\n")
67
+ end
68
+
69
+ include Spacialdb::Helpers
70
+
71
+ def credentials_file
72
+ if host == default_host
73
+ "#{home_directory}/.spacialdb/credentials"
74
+ else
75
+ "#{home_directory}/.spacialdb/credentials.#{host}"
76
+ end
77
+ end
78
+
79
+ def signup_for_credientials
80
+ puts "Sign up to Spacialdb."
81
+
82
+ print "Email: "
83
+ email = ask
84
+
85
+ print "Username: "
86
+ username = ask
87
+
88
+ print "Password: "
89
+ password = ask_for_password
90
+
91
+ print "Password confirmation: "
92
+ password_confirmation = ask_for_password
93
+
94
+ raise PasswordMismatch, "Password doesn't match confirmation." if password != password_confirmation
95
+
96
+ signup = Spacialdb::Client.signup(username, email, password, password_confirmation, host)
97
+ private_api_key = signup['private_api_key']
98
+ email = signup['email']
99
+
100
+ [email, private_api_key]
101
+ end
102
+
103
+ def ask_for_credentials
104
+ puts "Enter your Spacialdb credentials."
105
+
106
+ print "Email or Username: "
107
+ login = ask
108
+
109
+ print "Password: "
110
+ password = ask_for_password
111
+
112
+ auth = Spacialdb::Client.auth(login, password, host)
113
+ private_api_key = auth['private_api_key']
114
+ email = auth['email']
115
+
116
+ [email, private_api_key]
117
+ end
118
+
119
+ def ask_for_password
120
+ echo_off
121
+ password = ask
122
+ puts
123
+ echo_on
124
+ return password
125
+ end
126
+
127
+ def signup_and_save_credentials
128
+ begin
129
+ @credentials = signup_for_credientials
130
+ write_credentials
131
+ rescue ::RestClient::Unauthorized, ::RestClient::Forbidden => e
132
+ clear
133
+ display "Sign up failed:"
134
+ display e.response
135
+ exit 1
136
+ rescue PasswordMismatch => e
137
+ clear
138
+ display "Sign up failed:"
139
+ display e.message
140
+ exit 1
141
+ rescue Exception => e
142
+ delete_credentials
143
+ clear
144
+ display "Sign up failed:"
145
+ display e.message
146
+ raise e
147
+ end
148
+ end
149
+
150
+ def ask_for_and_save_credentials
151
+ begin
152
+ @credentials = ask_for_credentials
153
+ write_credentials
154
+ rescue ::RestClient::Unauthorized, ::RestClient::ResourceNotFound => e
155
+ delete_credentials
156
+ clear
157
+ display "Authentication failed."
158
+ exit 1
159
+ rescue Exception => e
160
+ delete_credentials
161
+ raise e
162
+ end
163
+ end
164
+
165
+ def write_credentials
166
+ FileUtils.mkdir_p(File.dirname(credentials_file))
167
+ f = File.open(credentials_file, 'w')
168
+ f.puts self.credentials
169
+ f.close
170
+ set_credentials_permissions
171
+ end
172
+
173
+ def set_credentials_permissions
174
+ FileUtils.chmod 0700, File.dirname(credentials_file)
175
+ FileUtils.chmod 0600, credentials_file
176
+ end
177
+
178
+ def delete_credentials
179
+ FileUtils.rm_f(credentials_file)
180
+ clear
181
+ end
182
+
183
+ def clear
184
+ @credentials = nil
185
+ @client = nil
186
+ end
187
+
188
+ end
189
+ end
@@ -0,0 +1,102 @@
1
+ require 'spacialdb/version'
2
+ require 'spacialdb/auth'
3
+ require 'spacialdb/helpers'
4
+ require 'rest_client'
5
+
6
+ class Spacialdb::Client
7
+
8
+ include Spacialdb::Helpers
9
+ extend Spacialdb::Helpers
10
+
11
+ def self.version
12
+ Spacialdb::VERSION
13
+ end
14
+
15
+ def self.gem_version_string
16
+ "spacialdb-gem/#{version}"
17
+ end
18
+
19
+ attr_accessor :host, :login, :password
20
+
21
+ def initialize(login, password, host=Spacialdb::Auth.default_host)
22
+ @login = login
23
+ @password = password
24
+ @host = host
25
+ end
26
+
27
+ def self.signup(username, email, password, password_confirmation, host=Spacialdb::Auth.default_host)
28
+ client = new(email, password, host)
29
+ json_decode client.post('/api/users',
30
+ { :email => email,
31
+ :username => username,
32
+ :password => password,
33
+ :password_confirmation => password_confirmation },
34
+ :accept => 'json').to_s
35
+ end
36
+
37
+ def self.auth(login, password, host=Spacialdb::Auth.default_host)
38
+ client = new(login, password, host)
39
+ json_decode client.get('/api/users/credentials', :accept => 'json').to_s
40
+ end
41
+
42
+ def list
43
+ json_decode get('/api/databases', :accept => 'json').to_s
44
+ end
45
+
46
+ def create
47
+ post('/api/databases', '', :accept => 'json')
48
+ end
49
+
50
+ # Destroy the database permanently.
51
+ def destroy(name)
52
+ delete("/api/databases/#{name}", :accept => 'json').to_s
53
+ end
54
+
55
+ def get(uri, extra_headers={}) # :nodoc:
56
+ process(:get, uri, extra_headers)
57
+ end
58
+
59
+ def post(uri, payload="", extra_headers={}) # :nodoc:
60
+ process(:post, uri, extra_headers, payload)
61
+ end
62
+
63
+ def delete(uri, extra_headers={}) # :nodoc:
64
+ process(:delete, uri, extra_headers)
65
+ end
66
+
67
+ def process(method, uri, extra_headers={}, payload=nil)
68
+ headers = spacialdb_headers.merge(extra_headers)
69
+ args = [method, payload, headers].compact
70
+ response = resource(uri).send(*args)
71
+
72
+ response
73
+ end
74
+
75
+ def spacialdb_headers # :nodoc:
76
+ {
77
+ 'X-Spacialdb-API-Version' => '1',
78
+ 'User-Agent' => self.class.gem_version_string,
79
+ 'X-Ruby-Version' => RUBY_VERSION,
80
+ 'X-Ruby-Platform' => RUBY_PLATFORM
81
+ }
82
+ end
83
+
84
+ def resource(uri)
85
+ RestClient.proxy = ENV['HTTP_PROXY'] || ENV['http_proxy']
86
+ resource = RestClient::Resource.new(realize_full_uri(uri),
87
+ :user => login,
88
+ :password => password
89
+ )
90
+ resource
91
+ end
92
+
93
+ def realize_full_uri(given)
94
+ full_host = (host =~ /^http/) ? host : "http://#{host}"
95
+ host = URI.parse(full_host)
96
+ uri = URI.parse(given)
97
+ uri.host ||= host.host
98
+ uri.scheme ||= host.scheme || "http"
99
+ uri.path = (uri.path[0..0] == "/") ? uri.path : "/#{uri.path}"
100
+ uri.to_s
101
+ end
102
+ end
@@ -0,0 +1,36 @@
1
+ require "spacialdb/command/base"
2
+
3
+ # authentication (signup, login, logout)
4
+ #
5
+ class Spacialdb::Command::Auth < Spacialdb::Command::Base
6
+
7
+ # auth:signup
8
+ #
9
+ # sign up for a free plan with Spacialdb
10
+ #
11
+ def signup
12
+ Spacialdb::Auth.signup
13
+ end
14
+
15
+ alias_command "signup", "auth:signup"
16
+
17
+ # auth:login
18
+ #
19
+ # log in with your Spacialdb credentials
20
+ #
21
+ def login
22
+ Spacialdb::Auth.login
23
+ end
24
+
25
+ alias_command "login", "auth:login"
26
+
27
+ # auth:logout
28
+ #
29
+ # clear local authentication credentials
30
+ #
31
+ def logout
32
+ Spacialdb::Auth.logout
33
+ end
34
+
35
+ alias_command "logout", "auth:logout"
36
+ end
@@ -0,0 +1,135 @@
1
+ require "fileutils"
2
+ require "spacialdb/client"
3
+ require "spacialdb/command"
4
+
5
+ class Spacialdb::Command::Base
6
+ include Spacialdb::Helpers
7
+
8
+ def self.namespace
9
+ self.to_s.split("::").last.downcase
10
+ end
11
+
12
+ attr_reader :args
13
+ attr_reader :options
14
+
15
+ def initialize(args=[], options={})
16
+ @args = args
17
+ @options = options
18
+ end
19
+
20
+ def spacialdb
21
+ Spacialdb::Auth.client
22
+ end
23
+
24
+ protected
25
+
26
+ def self.inherited(klass)
27
+ help = extract_help(*(caller.first.split(":")[0..1])).strip
28
+
29
+ Spacialdb::Command.register_namespace(
30
+ :name => klass.namespace,
31
+ :description => help.split("\n").first
32
+ )
33
+ end
34
+
35
+ def self.method_added(method)
36
+ return if self == Spacialdb::Command::Base
37
+ return if private_method_defined?(method)
38
+ return if protected_method_defined?(method)
39
+
40
+ help = extract_help(*(caller.first.split(":")[0..1]))
41
+
42
+ resolved_method = (method.to_s == "index") ? nil : method.to_s
43
+
44
+ default_command = [ self.namespace, resolved_method ].compact.join(":")
45
+ command = extract_command(help) || default_command
46
+
47
+ banner = extract_banner(help) || command
48
+ permute = !banner.index("*")
49
+ banner.gsub!("*", "")
50
+
51
+ Spacialdb::Command.register_command(
52
+ :klass => self,
53
+ :method => method,
54
+ :namespace => self.namespace,
55
+ :command => command,
56
+ :banner => banner,
57
+ :help => help,
58
+ :summary => extract_summary(help),
59
+ :description => extract_description(help),
60
+ :options => extract_options(help),
61
+ :permute => permute
62
+ )
63
+ end
64
+
65
+ def self.alias_command(new, old)
66
+ raise "no such command: #{old}" unless Spacialdb::Command.commands[old]
67
+ Spacialdb::Command.command_aliases[new] = old
68
+ end
69
+
70
+ def self.extract_help(file, line)
71
+ buffer = []
72
+ lines = File.read(file).split("\n")
73
+
74
+ catch(:done) do
75
+ (line.to_i-2).downto(1) do |i|
76
+ case lines[i].strip[0..0]
77
+ when "", "#" then buffer << lines[i]
78
+ else throw(:done)
79
+ end
80
+ end
81
+ end
82
+
83
+ buffer.map! do |line|
84
+ line.strip.gsub(/^#/, "")
85
+ end
86
+
87
+ buffer.reverse.join("\n").strip
88
+ end
89
+
90
+ def self.extract_command(help)
91
+ extract_banner(help).to_s.split(" ").first
92
+ end
93
+
94
+ def self.extract_banner(help)
95
+ help.split("\n").first
96
+ end
97
+
98
+ def self.extract_summary(help)
99
+ extract_description(help).split("\n").first
100
+ end
101
+
102
+ def self.extract_description(help)
103
+ lines = help.split("\n").map(&:strip)
104
+ lines.shift
105
+ lines.reject do |line|
106
+ line =~ /^-(.+)#(.+)/
107
+ end.join("\n").strip
108
+ end
109
+
110
+ def self.extract_options(help)
111
+ help.split("\n").map(&:strip).select do |line|
112
+ line =~ /^-(.+)#(.+)/
113
+ end.inject({}) do |hash, line|
114
+ description = line.split("#", 2).last.strip
115
+ long = line.match(/--([A-Za-z ]+)/)[1].strip
116
+ short = line.match(/-([A-Za-z ])/)[1].strip
117
+ hash.update(long.split(" ").first => { :desc => description, :short => short, :long => long })
118
+ end
119
+ end
120
+
121
+ def extract_option(name, default=true)
122
+ key = name.gsub("--", "").to_sym
123
+ return unless options[key]
124
+ value = options[key] || default
125
+ block_given? ? yield(value) : value
126
+ end
127
+
128
+ def extract_db
129
+ if options[:db].is_a?(String)
130
+ options[:db]
131
+ else
132
+ raise Spacialdb::Command::CommandFailed, "No db specified.\nSpecify which db to use with --db <db name>"
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,41 @@
1
+ require "spacialdb/command/base"
2
+
3
+ # manage databases (create, destroy)
4
+ #
5
+ class Spacialdb::Command::Db < Spacialdb::Command::Base
6
+
7
+ # db
8
+ #
9
+ # list your databases
10
+ #
11
+ def index
12
+ display spacialdb.list.join("\n")
13
+ end
14
+
15
+ alias_command "list", "db"
16
+
17
+ # db:create
18
+ #
19
+ # create your databases
20
+ #
21
+ def create
22
+ display spacialdb.create
23
+ end
24
+
25
+ alias_command "create", "db:create"
26
+
27
+ # db:destroy
28
+ #
29
+ # permanently destroy a database
30
+ #
31
+ def destroy
32
+ db = extract_db
33
+ if confirm_command(db)
34
+ redisplay "Destroying #{db} ... "
35
+ spacialdb.destroy(db)
36
+ display "done"
37
+ end
38
+ end
39
+
40
+ alias_command "destroy", "db:destroy"
41
+ end
@@ -0,0 +1,155 @@
1
+ require "spacialdb/command/base"
2
+
3
+ module Spacialdb::Command
4
+
5
+ # show this help
6
+ #
7
+ class Help < Base
8
+
9
+ class HelpGroup < Array
10
+ attr_reader :title
11
+
12
+ def initialize(title)
13
+ @title = title
14
+ end
15
+
16
+ def command(name, description)
17
+ self << [name, description]
18
+ end
19
+
20
+ def space
21
+ self << ['', '']
22
+ end
23
+ end
24
+
25
+ def self.groups
26
+ @groups ||= []
27
+ end
28
+
29
+ def self.group(title, &block)
30
+ groups << begin
31
+ group = HelpGroup.new(title)
32
+ yield group
33
+ group
34
+ end
35
+ end
36
+
37
+ def index
38
+ if command = args.shift
39
+ help_for_command(command)
40
+ else
41
+ help_for_root
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def commands_for_namespace(name)
48
+ Spacialdb::Command.commands.values.select do |command|
49
+ command[:namespace] == name && command[:method] != :index
50
+ end
51
+ end
52
+
53
+ def namespaces
54
+ namespaces = Spacialdb::Command.namespaces
55
+ end
56
+
57
+ def commands
58
+ commands = Spacialdb::Command.commands
59
+ Spacialdb::Command.command_aliases.each do |new, old|
60
+ commands[new] = commands[old].dup
61
+ commands[new][:banner] = "#{new} #{commands[new][:banner].split(" ", 2)[1]}"
62
+ commands[new][:command] = new
63
+ commands[new][:namespace] = nil
64
+ end
65
+ commands
66
+ end
67
+
68
+ def legacy_help_for_namespace(namespace)
69
+ instance = Spacialdb::Command::Help.groups.map do |group|
70
+ [ group.title, group.select { |c| c.first =~ /^#{namespace}/ }.length ]
71
+ end.sort_by(&:last).last
72
+ return nil unless instance
73
+ return nil if instance.last.zero?
74
+ instance.first
75
+ end
76
+
77
+ def legacy_help_for_command(command)
78
+ Spacialdb::Command::Help.groups.each do |group|
79
+ group.each do |cmd, description|
80
+ return description if cmd.split(" ").first == command
81
+ end
82
+ end
83
+ nil
84
+ end
85
+
86
+ PRIMARY_NAMESPACES = %w( auth db )
87
+
88
+ def primary_namespaces
89
+ PRIMARY_NAMESPACES.map { |name| namespaces[name] }.compact
90
+ end
91
+
92
+ def additional_namespaces
93
+ (namespaces.values - primary_namespaces).sort_by { |n| n[:name] }
94
+ end
95
+
96
+ def summary_for_namespaces(namespaces)
97
+ size = longest(namespaces.map { |n| n[:name] })
98
+ namespaces.each do |namespace|
99
+ name = namespace[:name]
100
+ namespace[:description] ||= legacy_help_for_namespace(name)
101
+ puts " %-#{size}s # %s" % [ name, namespace[:description] ]
102
+ end
103
+ end
104
+
105
+ def help_for_root
106
+ puts "Usage: spacialdb COMMAND [command-specific-options]"
107
+ puts
108
+ puts "Primary help topics, type \"spacialdb help TOPIC\" for more details:"
109
+ puts
110
+ summary_for_namespaces(primary_namespaces)
111
+ puts
112
+ puts "Additional topics:"
113
+ puts
114
+ summary_for_namespaces(additional_namespaces)
115
+ puts
116
+ end
117
+
118
+ def help_for_namespace(name)
119
+ namespace_commands = commands_for_namespace(name)
120
+
121
+ unless namespace_commands.empty?
122
+ size = longest(namespace_commands.map { |c| c[:banner] })
123
+ namespace_commands.sort_by { |c| c[:method].to_s }.each do |command|
124
+ next if command[:help] =~ /DEPRECATED/
125
+ command[:summary] ||= legacy_help_for_command(command[:command])
126
+ puts " %-#{size}s # %s" % [ command[:banner], command[:summary] ]
127
+ end
128
+ end
129
+ end
130
+
131
+ def help_for_command(name)
132
+ command = commands[name]
133
+
134
+ if command
135
+ if command[:help].strip.length > 0
136
+ print "Usage: spacialdb #{command[:banner]}"
137
+ puts command[:help].split("\n")[1..-1].join("\n")
138
+ puts
139
+ else
140
+ puts "Usage: spacialdb #{command[:banner]}"
141
+ puts
142
+ puts " " + legacy_help_for_command(name).to_s
143
+ puts
144
+ end
145
+ end
146
+
147
+ unless commands_for_namespace(name).empty?
148
+ puts "Additional commands, type \"spacialdb help COMMAND\" for more details:"
149
+ puts
150
+ help_for_namespace(name)
151
+ puts
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,16 @@
1
+ require "spacialdb/command/base"
2
+
3
+ # display version
4
+ #
5
+ module Spacialdb::Command
6
+
7
+ # version
8
+ #
9
+ # show the spacialdb gem version
10
+ #
11
+ class Version < Base
12
+ def index
13
+ display Spacialdb::Client.gem_version_string
14
+ end
15
+ end
16
+ end