lingohub 0.0.4

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.
@@ -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