lingohub 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ ## 0.0.4
2
+
3
+ * added command line help for: login, logout
4
+ * connects to 'lingohub.com' by default
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011-2012 lingohub GmbH
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,10 @@
1
+ ## lingohub ruby
2
+
3
+ ### Maintainers
4
+
5
+ * Markus Merzinger (https://github.com/maerzbow)
6
+ * Helmut Juskewycz (https://github.com/hjuskewycz)
7
+
8
+ ## License
9
+
10
+ MIT License. Copyright 2012 lingohub GmbH. http://lingohub.com
data/REST.md ADDED
File without changes
File without changes
@@ -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 'lingohub'
7
+ require 'lingohub/command'
8
+
9
+ args = ARGV.dup
10
+ ARGV.clear
11
+ command = args.shift.strip rescue 'help'
12
+
13
+ Lingohub::Command.run(command, args)
14
+
@@ -0,0 +1,16 @@
1
+ require "lingohub/client"
2
+ require "lingohub/rails3/railtie" if defined?(Rails)
3
+
4
+ module Lingohub
5
+ class << self
6
+ attr_accessor :environments, :protocol, :host, :username, :project
7
+
8
+ def configure
9
+ yield self
10
+ end
11
+
12
+ def default_value?(value)
13
+ value.start_with?(":")
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,106 @@
1
+ require 'rest_client'
2
+ require 'uri'
3
+ require 'time'
4
+ require 'lingohub/version'
5
+ require 'vendor/okjson'
6
+ require 'json'
7
+ require 'lingohub/models/projects'
8
+
9
+ # A Ruby class to call the lingohub REST API. You might use this if you want to
10
+ # manage your lingohub apps from within a Ruby program, such as Capistrano.
11
+ #
12
+ # Example:
13
+ #
14
+ # require 'lingohub'
15
+ # lingohub = Lingohub::Client.new('me@example.com', 'mypass')
16
+ # lingohub.create('myapp')
17
+ #
18
+ class Lingohub::Client
19
+
20
+ def self.version
21
+ Lingohub::VERSION
22
+ end
23
+
24
+ def self.gem_version_string
25
+ "lingohub-gem/#{version}"
26
+ end
27
+
28
+ attr_accessor :host, :user, :password
29
+
30
+ def self.auth(options)
31
+ client = new(options)
32
+ OkJson.decode client.post('/sessions', {}, :accept => 'json').to_s
33
+ end
34
+
35
+ def initialize(options)
36
+ @user = options[:username]
37
+ @password = options[:password]
38
+ @auth_token = options[:auth_token]
39
+ @host = options[:host] || 'lingohub.com'
40
+ end
41
+
42
+ def credentials
43
+ @auth_token.nil? ? {:username => @user, :password => @password} : {:username => @auth_token, :password => ""}
44
+ end
45
+
46
+ def project(title)
47
+ project = self.projects[title]
48
+ raise(Lingohub::Command::CommandFailed, "=== You aren't associated for a project named '#{title}'") if project.nil?
49
+ project
50
+ end
51
+
52
+ def projects
53
+ return Lingohub::Models::Projects.new(self)
54
+ end
55
+
56
+ def get(uri, extra_headers={ }) # :nodoc:
57
+ process(:get, uri, extra_headers)
58
+ end
59
+
60
+ def post(uri, payload="", extra_headers={ }) # :nodoc:
61
+ process(:post, uri, extra_headers, payload)
62
+ end
63
+
64
+ def put(uri, payload, extra_headers={ }) # :nodoc:
65
+ process(:put, uri, extra_headers, payload)
66
+ end
67
+
68
+ def delete(uri, extra_headers={ }) # :nodoc:
69
+ process(:delete, uri, extra_headers)
70
+ end
71
+
72
+ def process(method, uri, extra_headers={ }, payload=nil)
73
+ headers = lingohub_headers.merge(extra_headers)
74
+ # payload = auth_params.merge(payload)
75
+ args = [method, payload, headers].compact
76
+ response = resource(uri, credentials).send(*args)
77
+
78
+ response
79
+ end
80
+
81
+ def resource(uri, credentials)
82
+ RestClient.proxy = ENV['HTTP_PROXY'] || ENV['http_proxy']
83
+ if uri =~ /^https?/
84
+ RestClient::Resource.new(uri, :user => credentials[:username], :password => credentials[:password])
85
+ else
86
+ host_uri = host =~ /^https?/ ? "#{host}/#{api_uri_part}" : "https://#{host}/#{api_uri_part}"
87
+ RestClient::Resource.new(host_uri, :user => credentials[:username], :password => credentials[:password])[uri]
88
+ end
89
+ end
90
+
91
+ def api_uri_part
92
+ "api/#{Lingohub::API_VERSION}"
93
+ end
94
+
95
+ def lingohub_headers # :nodoc:
96
+ {
97
+ 'X-lingohub-API-Version' => '1',
98
+ 'User-Agent' => self.class.gem_version_string,
99
+ 'X-Ruby-Version' => RUBY_VERSION,
100
+ 'X-Ruby-Platform' => RUBY_PLATFORM,
101
+ 'content_type' => 'json',
102
+ 'accept' => 'json'
103
+ }
104
+ end
105
+
106
+ end
@@ -0,0 +1,98 @@
1
+ require 'lingohub/helpers'
2
+ require 'lingohub/commands/base'
3
+
4
+ Dir["#{File.dirname(__FILE__)}/commands/*.rb"].each { |c| require c }
5
+
6
+ module Lingohub
7
+ module Command
8
+ class InvalidCommand < RuntimeError; end
9
+ class CommandFailed < RuntimeError; end
10
+
11
+ extend Lingohub::Helpers
12
+
13
+ class << self
14
+
15
+ def run(command, args, retries=0)
16
+ begin
17
+ run_internal 'auth:reauthorize', args.dup if retries > 0
18
+ run_internal(command, args.dup)
19
+ rescue InvalidCommand
20
+ error "Unknown command. Run 'lingohub help' for usage information."
21
+ # rescue RestClient::Unauthorized
22
+ # if retries < 3
23
+ # STDERR.puts "Authentication failure"
24
+ # run(command, args, retries+1)
25
+ # else
26
+ # error "Authentication failure"
27
+ # end
28
+ # rescue RestClient::ResourceNotFound => e
29
+ # error extract_not_found(e.http_body)
30
+ # rescue RestClient::RequestFailed => e
31
+ # error extract_error(e.http_body) unless e.http_code == 402
32
+ # retry if run_internal('account:confirm_billing', args.dup)
33
+ # rescue RestClient::RequestTimeout
34
+ # error "API request timed out. Please try again, or contact team@lingohub.com if this issue persists."
35
+ rescue CommandFailed => e
36
+ error e.message
37
+ rescue Interrupt => e
38
+ error "\n[canceled]"
39
+ end
40
+ end
41
+
42
+ def run_internal(command, args, lingohub=nil)
43
+ klass, method = parse(command)
44
+ runner = klass.new(args, lingohub)
45
+ raise InvalidCommand unless runner.respond_to?(method)
46
+ runner.send(method)
47
+ end
48
+
49
+ def parse(command)
50
+ parts = command.split(':')
51
+ case parts.size
52
+ when 1
53
+ begin
54
+ return eval("Lingohub::Command::#{command.capitalize}"), :index
55
+ rescue NameError, NoMethodError
56
+ return Lingohub::Command::Project, command.to_sym
57
+ end
58
+ else
59
+ begin
60
+ const = Lingohub::Command
61
+ command = parts.pop
62
+ parts.each { |part| const = const.const_get(part.capitalize) }
63
+ return const, command.to_sym
64
+ rescue NameError
65
+ raise InvalidCommand
66
+ end
67
+ end
68
+ end
69
+
70
+ # def extract_not_found(body)
71
+ # body =~ /^[\w\s]+ not found$/ ? body : "Resource not found"
72
+ # end
73
+ #
74
+ # def extract_error(body)
75
+ # msg = parse_error_xml(body) || parse_error_json(body) || parse_error_plain(body) || 'Internal server error'
76
+ # msg.split("\n").map { |line| ' ! ' + line }.join("\n")
77
+ # end
78
+ #
79
+ # def parse_error_xml(body)
80
+ # xml_errors = REXML::Document.new(body).elements.to_a("//errors/error")
81
+ # msg = xml_errors.map { |a| a.text }.join(" / ")
82
+ # return msg unless msg.empty?
83
+ # rescue Exception
84
+ # end
85
+ #
86
+ # def parse_error_json(body)
87
+ # json = OkJson.decode(body.to_s)
88
+ # json['error']
89
+ # rescue OkJson::ParserError
90
+ # end
91
+ #
92
+ # def parse_error_plain(body)
93
+ # return unless body.respond_to?(:headers) && body.headers[:content_type].include?("text/plain")
94
+ # body.to_s
95
+ # end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,146 @@
1
+ require "lingohub/client"
2
+
3
+ module Lingohub::Command
4
+ class Auth < Base
5
+ attr_accessor :credentials
6
+
7
+ def client
8
+ @client ||= init_lingohub
9
+ end
10
+
11
+ def init_lingohub
12
+ client = Lingohub::Client.new(:username => user, :auth_token => auth_token, :host => host)
13
+ # client.on_warning { |msg| self.display("\n#{msg}\n\n") }
14
+ client
15
+ end
16
+
17
+ def host
18
+ ENV['lingohub_HOST'] || 'lingohub.com'
19
+ end
20
+
21
+ # just a stub; will raise if not authenticated
22
+ def check
23
+ client.projects.all
24
+ end
25
+
26
+ def reauthorize
27
+ @credentials = ask_for_and_save_credentials
28
+ end
29
+
30
+ def user # :nodoc:
31
+ get_credentials
32
+ @credentials[0]
33
+ end
34
+
35
+ def auth_token # :nodoc:
36
+ get_credentials
37
+ @credentials[1]
38
+ end
39
+
40
+ def credentials_file
41
+ "#{home_directory}/.lingohub/credentials"
42
+ end
43
+
44
+ def get_credentials # :nodoc:
45
+ return if @credentials
46
+ unless @credentials = read_credentials
47
+ ask_for_and_save_credentials
48
+ end
49
+ @credentials
50
+ end
51
+
52
+ def read_credentials
53
+ File.exists?(credentials_file) and File.read(credentials_file).split("\n")
54
+ end
55
+
56
+ def echo_off
57
+ system "stty -echo"
58
+ end
59
+
60
+ def echo_on
61
+ system "stty echo"
62
+ end
63
+
64
+ def ask_for_credentials
65
+ puts "Enter your Lingohub credentials."
66
+
67
+ print "Email: "
68
+ user = ask
69
+
70
+ print "Password: "
71
+ password = running_on_windows? ? ask_for_password_on_windows : ask_for_password
72
+ api_key = Lingohub::Client.auth(:username => user, :password => password, :host => host)['api_key']
73
+
74
+ [user, api_key]
75
+ end
76
+
77
+ def ask_for_password_on_windows
78
+ require "Win32API"
79
+ char = nil
80
+ password = ''
81
+
82
+ while char = Win32API.new("crtdll", "_getch", [], "L").Call do
83
+ break if char == 10 || char == 13 # received carriage return or newline
84
+ if char == 127 || char == 8 # backspace and delete
85
+ password.slice!(-1, 1)
86
+ else
87
+ # windows might throw a -1 at us so make sure to handle RangeError
88
+ (password << char.chr) rescue RangeError
89
+ end
90
+ end
91
+ puts
92
+ return password
93
+ end
94
+
95
+ def ask_for_password
96
+ echo_off
97
+ password = ask
98
+ puts
99
+ echo_on
100
+ return password
101
+ end
102
+
103
+ def ask_for_and_save_credentials
104
+ begin
105
+ @credentials = ask_for_credentials
106
+ write_credentials
107
+ check
108
+ rescue ::RestClient::Unauthorized, ::RestClient::ResourceNotFound => e
109
+ puts "EXCEPTION #{e}"
110
+ delete_credentials
111
+ @client = nil
112
+ @credentials = nil
113
+ display "Authentication failed."
114
+ retry if retry_login?
115
+ exit 1
116
+ rescue Exception => e
117
+ delete_credentials
118
+ raise e
119
+ end
120
+ end
121
+
122
+ def retry_login?
123
+ @login_attempts ||= 0
124
+ @login_attempts += 1
125
+ @login_attempts < 3
126
+ end
127
+
128
+ def write_credentials
129
+ FileUtils.mkdir_p(File.dirname(credentials_file))
130
+ f = File.open(credentials_file, 'w')
131
+ f.chmod(0600)
132
+ f.puts self.credentials
133
+ f.close
134
+ set_credentials_permissions
135
+ end
136
+
137
+ def set_credentials_permissions
138
+ FileUtils.chmod 0700, File.dirname(credentials_file)
139
+ FileUtils.chmod 0600, credentials_file
140
+ end
141
+
142
+ def delete_credentials
143
+ # FileUtils.rm_f(credentials_file)
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,119 @@
1
+ require 'fileutils'
2
+
3
+ module Lingohub::Command
4
+ class Base
5
+ include Lingohub::Helpers
6
+
7
+ attr_accessor :args
8
+ attr_reader :autodetected_app
9
+
10
+ def initialize(args, lingohub=nil)
11
+ @args = args
12
+ @lingohub = lingohub
13
+ @autodetected_project_name = false
14
+ end
15
+
16
+ def lingohub
17
+ @lingohub ||= Lingohub::Command.run_internal('auth:client', args)
18
+ end
19
+
20
+ def project_title(force=true)
21
+ project_title = extract_project_title_from_args
22
+ unless project_title
23
+ project_title = extract_project_title_from_git || extract_project_title_from_dir_name ||
24
+ raise(CommandFailed, "No project specified.\nRun this command from project folder or set it adding --project <title>") if force
25
+ @autodetected_project_name = true
26
+ end
27
+ project_title
28
+ end
29
+
30
+ def extract_project_title_from_args
31
+ project_title = extract_option('--project', false)
32
+ raise(CommandFailed, "You must specify a project title after --project") if project_title == false
33
+ project_title
34
+ end
35
+
36
+ def extract_project_title_from_dir_name
37
+ dir = Dir.pwd
38
+ File.basename(dir)
39
+ end
40
+
41
+ def extract_project_title_from_git
42
+ dir = Dir.pwd
43
+ return unless remotes = git_remotes(dir)
44
+
45
+ if remote = extract_option('--remote')
46
+ remotes[remote]
47
+ elsif remote = extract_app_from_git_config
48
+ remotes[remote]
49
+ else
50
+ apps = remotes.values.uniq
51
+ return apps.first if apps.size == 1
52
+ end
53
+ end
54
+
55
+ def extract_app_from_git_config
56
+ remote = git("config heroku.remote")
57
+ remote == "" ? nil : remote
58
+ end
59
+
60
+ def git_remotes(base_dir=Dir.pwd)
61
+ remotes = { }
62
+ original_dir = Dir.pwd
63
+ Dir.chdir(base_dir)
64
+
65
+ # TODO
66
+ # git("remote -v").split("\n").each do |remote|
67
+ # name, url, method = remote.split(/\s/)
68
+ # if url =~ /^git@#{heroku.host}:([\w\d-]+)\.git$/
69
+ # remotes[name] = $1
70
+ # end
71
+ # end
72
+
73
+ Dir.chdir(original_dir)
74
+ remotes
75
+ end
76
+
77
+ def extract_option(options, default=true)
78
+ values = options.is_a?(Array) ? options : [options]
79
+ return unless opt_index = args.select { |a| values.include? a }.first
80
+ opt_position = args.index(opt_index) + 1
81
+ if args.size > opt_position && opt_value = args[opt_position]
82
+ if opt_value.include?('--')
83
+ opt_value = nil
84
+ else
85
+ args.delete_at(opt_position)
86
+ end
87
+ end
88
+ opt_value ||= default
89
+ args.delete(opt_index)
90
+ block_given? ? yield(opt_value) : opt_value
91
+ end
92
+
93
+ def git_url(name)
94
+ "git@#{heroku.host}:#{name}.git"
95
+ end
96
+
97
+ def app_urls(name)
98
+ # "#{web_url(name)} | #{git_url(name)}"
99
+ end
100
+
101
+ def escape(value)
102
+ lingohub.escape(value)
103
+ end
104
+
105
+ def project(title=nil)
106
+ title ||= project_title
107
+ @project ||= lingohub.project(title)
108
+ end
109
+ end
110
+
111
+ class BaseWithApp < Base
112
+ attr_accessor :app
113
+
114
+ def initialize(args, lingohub=nil)
115
+ super(args, lingohub)
116
+ @app ||= extract_app
117
+ end
118
+ end
119
+ end