linguist_ruby 0.0.2
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/CHANGELOG.md +0 -0
- data/LICENSE +20 -0
- data/README.md +0 -0
- data/REST.md +0 -0
- data/ROADMAP.md +0 -0
- data/bin/linguist +14 -0
- data/lib/linguist/client.rb +120 -0
- data/lib/linguist/command.rb +98 -0
- data/lib/linguist/commands/app.rb +72 -0
- data/lib/linguist/commands/auth.rb +146 -0
- data/lib/linguist/commands/base.rb +118 -0
- data/lib/linguist/commands/help.rb +90 -0
- data/lib/linguist/commands/translations.rb +30 -0
- data/lib/linguist/commands/version.rb +7 -0
- data/lib/linguist/helpers.rb +121 -0
- data/lib/linguist/models/project.rb +81 -0
- data/lib/linguist/models/projects.rb +34 -0
- data/lib/linguist/models/resource.rb +26 -0
- data/lib/linguist/models/user.rb +3 -0
- data/lib/linguist/version.rb +3 -0
- data/lib/linguist.rb +3 -0
- data/lib/vendor/okjson.rb +556 -0
- metadata +154 -0
data/CHANGELOG.md
ADDED
File without changes
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Carl Lerche, Yehuda Katz
|
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.
|
data/README.md
ADDED
File without changes
|
data/REST.md
ADDED
File without changes
|
data/ROADMAP.md
ADDED
File without changes
|
data/bin/linguist
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 'linguist'
|
7
|
+
require 'linguist/command'
|
8
|
+
|
9
|
+
args = ARGV.dup
|
10
|
+
ARGV.clear
|
11
|
+
command = args.shift.strip rescue 'help'
|
12
|
+
|
13
|
+
Linguist::Command.run(command, args)
|
14
|
+
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'rest_client'
|
2
|
+
require 'uri'
|
3
|
+
require 'time'
|
4
|
+
require 'linguist/version'
|
5
|
+
require 'vendor/okjson'
|
6
|
+
require 'json'
|
7
|
+
require 'linguist/models/projects'
|
8
|
+
|
9
|
+
# A Ruby class to call the Linguist REST API. You might use this if you want to
|
10
|
+
# manage your Linguist apps from within a Ruby program, such as Capistrano.
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
#
|
14
|
+
# require 'linguist'
|
15
|
+
# linguist = Linguist::Client.new('me@example.com', 'mypass')
|
16
|
+
# linguist.create('myapp')
|
17
|
+
#
|
18
|
+
class Linguist::Client
|
19
|
+
|
20
|
+
def self.version
|
21
|
+
Linguist::VERSION
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.gem_version_string
|
25
|
+
"linguist-gem/#{version}"
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_accessor :host, :user, :auth_token
|
29
|
+
|
30
|
+
def self.auth(user, password, host='lingui.st')
|
31
|
+
client = new(user, password, host)
|
32
|
+
OkJson.decode client.post('/sessions', { :email => user, :password => password }, :accept => 'json').to_s
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize(user, auth_token, host='lingui.st')
|
36
|
+
@user = user
|
37
|
+
@auth_token = auth_token
|
38
|
+
@host = host
|
39
|
+
end
|
40
|
+
|
41
|
+
def project(title)
|
42
|
+
project = self.projects[title]
|
43
|
+
raise(CommandFailed, "=== You aren't associated for a project named '#{title}'") if project.nil?
|
44
|
+
project
|
45
|
+
end
|
46
|
+
|
47
|
+
def projects
|
48
|
+
return Linguist::Models::Projects.new(self)
|
49
|
+
end
|
50
|
+
|
51
|
+
def get(uri, extra_headers={ }) # :nodoc:
|
52
|
+
process(:get, uri, extra_headers)
|
53
|
+
end
|
54
|
+
|
55
|
+
def post(uri, payload="", extra_headers={ }) # :nodoc:
|
56
|
+
process(:post, uri, extra_headers, payload)
|
57
|
+
end
|
58
|
+
|
59
|
+
def put(uri, payload, extra_headers={ }) # :nodoc:
|
60
|
+
process(:put, 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 = linguist_headers.merge(extra_headers)
|
69
|
+
# payload = auth_params.merge(payload)
|
70
|
+
args = [method, payload, headers].compact
|
71
|
+
response = resource(uri).send(*args)
|
72
|
+
|
73
|
+
puts "RESPONSE #{response}"
|
74
|
+
|
75
|
+
# extract_warning(response)
|
76
|
+
response
|
77
|
+
end
|
78
|
+
|
79
|
+
def resource(uri)
|
80
|
+
|
81
|
+
RestClient.proxy = ENV['HTTP_PROXY'] || ENV['http_proxy']
|
82
|
+
if uri =~ /^https?/
|
83
|
+
# RestClient::Resource.new(uri, user, auth_token)
|
84
|
+
RestClient::Resource.new(uri)
|
85
|
+
elsif host =~ /^https?/
|
86
|
+
# RestClient::Resource.new(host, user, auth_token)[uri]
|
87
|
+
RestClient::Resource.new(host)[uri]
|
88
|
+
else
|
89
|
+
# RestClient::Resource.new("https://api.#{host}", user, password)[uri]
|
90
|
+
# RestClient::Resource.new("http://localhost:3000/api/v1", user, auth_token)[uri]
|
91
|
+
RestClient::Resource.new("http://localhost:3000/api/v1")[uri]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def extract_warning(response)
|
96
|
+
# return unless response
|
97
|
+
# if response.headers[:x_heroku_warning] && @warning_callback
|
98
|
+
# warning = response.headers[:x_heroku_warning]
|
99
|
+
# @displayed_warnings ||= { }
|
100
|
+
# unless @displayed_warnings[warning]
|
101
|
+
# @warning_callback.call(warning)
|
102
|
+
# @displayed_warnings[warning] = true
|
103
|
+
# end
|
104
|
+
# end
|
105
|
+
end
|
106
|
+
|
107
|
+
def linguist_headers # :nodoc:
|
108
|
+
{
|
109
|
+
'X-Linguist-API-Version' => '1',
|
110
|
+
'X-Linguist-User-Email' => user,
|
111
|
+
'X-Linguist-User-Auth-Token' => auth_token,
|
112
|
+
'User-Agent' => self.class.gem_version_string,
|
113
|
+
'X-Ruby-Version' => RUBY_VERSION,
|
114
|
+
'X-Ruby-Platform' => RUBY_PLATFORM,
|
115
|
+
'content_type' => 'json',
|
116
|
+
'accept' => 'json'
|
117
|
+
}
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'linguist/helpers'
|
2
|
+
require 'linguist/commands/base'
|
3
|
+
|
4
|
+
Dir["#{File.dirname(__FILE__)}/commands/*.rb"].each { |c| require c }
|
5
|
+
|
6
|
+
module Linguist
|
7
|
+
module Command
|
8
|
+
class InvalidCommand < RuntimeError; end
|
9
|
+
class CommandFailed < RuntimeError; end
|
10
|
+
|
11
|
+
extend Linguist::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 'linguist 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 support@lingui.st 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, linguist=nil)
|
43
|
+
klass, method = parse(command)
|
44
|
+
runner = klass.new(args, linguist)
|
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("Linguist::Command::#{command.capitalize}"), :index
|
55
|
+
rescue NameError, NoMethodError
|
56
|
+
return Linguist::Command::App, command.to_sym
|
57
|
+
end
|
58
|
+
else
|
59
|
+
begin
|
60
|
+
const = Linguist::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,72 @@
|
|
1
|
+
require 'readline'
|
2
|
+
require 'launchy'
|
3
|
+
|
4
|
+
module Linguist::Command
|
5
|
+
class App < Base
|
6
|
+
def login
|
7
|
+
Linguist::Command.run_internal "auth:reauthorize", args.dup
|
8
|
+
end
|
9
|
+
|
10
|
+
def logout
|
11
|
+
Linguist::Command.run_internal "auth:delete_credentials", args.dup
|
12
|
+
display "Local credentials cleared."
|
13
|
+
end
|
14
|
+
|
15
|
+
def list
|
16
|
+
list = linguist.projects.all
|
17
|
+
if list.size > 0
|
18
|
+
display "Projects:\n" + list.keys.map { |name|
|
19
|
+
"- #{name}"
|
20
|
+
}.join("\n")
|
21
|
+
else
|
22
|
+
display "You have no projects."
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def create
|
27
|
+
title = args.shift.downcase.strip rescue nil
|
28
|
+
title ||= extract_from_dir_name
|
29
|
+
linguist.projects.create title
|
30
|
+
display("Created #{title}")
|
31
|
+
end
|
32
|
+
|
33
|
+
def rename
|
34
|
+
newtitle = args.shift.downcase.strip rescue ''
|
35
|
+
oldtitle = project.title
|
36
|
+
|
37
|
+
raise(CommandFailed, "Invalid name.") if newtitle == ''
|
38
|
+
|
39
|
+
project.update(:title => newtitle)
|
40
|
+
display("Renaming project from #{oldtitle} to #{newtitle}")
|
41
|
+
|
42
|
+
display app_urls(newtitle)
|
43
|
+
end
|
44
|
+
|
45
|
+
def info
|
46
|
+
display "=== #{project.title}"
|
47
|
+
display "Web URL: #{project.weburl}"
|
48
|
+
display "Owner: #{project.owner}"
|
49
|
+
display "Number of translation: #{project.translations_count}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def open
|
53
|
+
project = linguist.project
|
54
|
+
url = project.weburl
|
55
|
+
Launchy.open url
|
56
|
+
end
|
57
|
+
|
58
|
+
def destroy
|
59
|
+
display "=== #{project.title}"
|
60
|
+
display "Web URL: #{project.weburl}"
|
61
|
+
display "Owner: #{project.owner}"
|
62
|
+
|
63
|
+
if confirm_command(project_title)
|
64
|
+
redisplay "Destroying #{project_title} ... "
|
65
|
+
project.destroy
|
66
|
+
display "done"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require "linguist/client"
|
2
|
+
|
3
|
+
module Linguist::Command
|
4
|
+
class Auth < Base
|
5
|
+
attr_accessor :credentials
|
6
|
+
|
7
|
+
def client
|
8
|
+
@client ||= init_linguist
|
9
|
+
end
|
10
|
+
|
11
|
+
def init_linguist
|
12
|
+
client = Linguist::Client.new(user, auth_token, host)
|
13
|
+
# client.on_warning { |msg| self.display("\n#{msg}\n\n") }
|
14
|
+
client
|
15
|
+
end
|
16
|
+
|
17
|
+
def host
|
18
|
+
ENV['LINGUIST_HOST'] || 'lingui.st'
|
19
|
+
end
|
20
|
+
|
21
|
+
# just a stub; will raise if not authenticated
|
22
|
+
def check
|
23
|
+
client.list
|
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}/.linguist/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 Linguist credentials."
|
66
|
+
|
67
|
+
print "Email: "
|
68
|
+
user = "hjuskewycz@hemju.com"#ask
|
69
|
+
|
70
|
+
print "Password: "
|
71
|
+
password = "testtest"#running_on_windows? ? ask_for_password_on_windows : ask_for_password
|
72
|
+
api_key = Linguist::Client.auth(user, password, 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,118 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Linguist::Command
|
4
|
+
class Base
|
5
|
+
include Linguist::Helpers
|
6
|
+
|
7
|
+
attr_accessor :args
|
8
|
+
attr_reader :autodetected_app
|
9
|
+
|
10
|
+
def initialize(args, linguist=nil)
|
11
|
+
@args = args
|
12
|
+
@linguist = linguist
|
13
|
+
@autodetected_app = false
|
14
|
+
end
|
15
|
+
|
16
|
+
def linguist
|
17
|
+
@linguist ||= Linguist::Command.run_internal('auth:client', args)
|
18
|
+
end
|
19
|
+
|
20
|
+
def extract_app(force=true)
|
21
|
+
app = extract_option('--app', false)
|
22
|
+
raise(CommandFailed, "You must specify an app name after --app") if app == false
|
23
|
+
unless app
|
24
|
+
app = extract_app_from_git || extract_from_dir_name ||
|
25
|
+
raise(CommandFailed, "No app specified.\nRun this command from app folder or set it adding --app <app name>") if force
|
26
|
+
@autodetected_app = true
|
27
|
+
end
|
28
|
+
app
|
29
|
+
end
|
30
|
+
|
31
|
+
def extract_from_dir_name
|
32
|
+
dir = Dir.pwd
|
33
|
+
File.basename(dir)
|
34
|
+
end
|
35
|
+
|
36
|
+
def extract_app_from_git
|
37
|
+
dir = Dir.pwd
|
38
|
+
return unless remotes = git_remotes(dir)
|
39
|
+
|
40
|
+
if remote = extract_option('--remote')
|
41
|
+
remotes[remote]
|
42
|
+
elsif remote = extract_app_from_git_config
|
43
|
+
remotes[remote]
|
44
|
+
else
|
45
|
+
apps = remotes.values.uniq
|
46
|
+
return apps.first if apps.size == 1
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def extract_app_from_git_config
|
51
|
+
remote = git("config heroku.remote")
|
52
|
+
remote == "" ? nil : remote
|
53
|
+
end
|
54
|
+
|
55
|
+
def git_remotes(base_dir=Dir.pwd)
|
56
|
+
remotes = { }
|
57
|
+
original_dir = Dir.pwd
|
58
|
+
Dir.chdir(base_dir)
|
59
|
+
|
60
|
+
# TODO
|
61
|
+
# git("remote -v").split("\n").each do |remote|
|
62
|
+
# name, url, method = remote.split(/\s/)
|
63
|
+
# if url =~ /^git@#{heroku.host}:([\w\d-]+)\.git$/
|
64
|
+
# remotes[name] = $1
|
65
|
+
# end
|
66
|
+
# end
|
67
|
+
|
68
|
+
Dir.chdir(original_dir)
|
69
|
+
remotes
|
70
|
+
end
|
71
|
+
|
72
|
+
def extract_option(options, default=true)
|
73
|
+
values = options.is_a?(Array) ? options : [options]
|
74
|
+
return unless opt_index = args.select { |a| values.include? a }.first
|
75
|
+
opt_position = args.index(opt_index) + 1
|
76
|
+
if args.size > opt_position && opt_value = args[opt_position]
|
77
|
+
if opt_value.include?('--')
|
78
|
+
opt_value = nil
|
79
|
+
else
|
80
|
+
args.delete_at(opt_position)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
opt_value ||= default
|
84
|
+
args.delete(opt_index)
|
85
|
+
block_given? ? yield(opt_value) : opt_value
|
86
|
+
end
|
87
|
+
|
88
|
+
def git_url(name)
|
89
|
+
"git@#{heroku.host}:#{name}.git"
|
90
|
+
end
|
91
|
+
|
92
|
+
def app_urls(name)
|
93
|
+
# "#{web_url(name)} | #{git_url(name)}"
|
94
|
+
end
|
95
|
+
|
96
|
+
def escape(value)
|
97
|
+
linguist.escape(value)
|
98
|
+
end
|
99
|
+
|
100
|
+
def project(title=nil)
|
101
|
+
title ||= project_title
|
102
|
+
@project ||= linguist.project(title)
|
103
|
+
end
|
104
|
+
|
105
|
+
def project_title
|
106
|
+
(args.first && !args.first =~ /^\-\-/) ? args.first : extract_app
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class BaseWithApp < Base
|
111
|
+
attr_accessor :app
|
112
|
+
|
113
|
+
def initialize(args, linguist=nil)
|
114
|
+
super(args, linguist)
|
115
|
+
@app ||= extract_app
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Linguist::Command
|
2
|
+
class Help < Base
|
3
|
+
class HelpGroup < Array
|
4
|
+
|
5
|
+
attr_reader :title
|
6
|
+
|
7
|
+
def initialize(title)
|
8
|
+
@title = title
|
9
|
+
end
|
10
|
+
|
11
|
+
def command(name, description)
|
12
|
+
self << [name, description]
|
13
|
+
end
|
14
|
+
|
15
|
+
def space
|
16
|
+
self << ['', '']
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.groups
|
21
|
+
@groups ||= []
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.group(title, &block)
|
25
|
+
groups << begin
|
26
|
+
group = HelpGroup.new(title)
|
27
|
+
yield group
|
28
|
+
group
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.create_default_groups!
|
33
|
+
return if @defaults_created
|
34
|
+
@defaults_created = true
|
35
|
+
group 'General Commands' do |group|
|
36
|
+
group.command 'help', 'show this usage'
|
37
|
+
group.command 'version', 'show the gem version'
|
38
|
+
group.space
|
39
|
+
# group.command 'login', 'log in with your linguist credentials'
|
40
|
+
# group.command 'logout', 'clear local authentication credentials'
|
41
|
+
# group.space
|
42
|
+
group.command 'list', 'list your projects'
|
43
|
+
# group.command 'create [<name>]', 'create a new app'
|
44
|
+
group.command 'info', 'show app info, like web url and number of translations'
|
45
|
+
group.command 'open', 'open the app in a web browser'
|
46
|
+
group.command 'rename <newname>', 'rename the app'
|
47
|
+
group.command 'destroy', 'destroy the app permanently'
|
48
|
+
group.space
|
49
|
+
end
|
50
|
+
|
51
|
+
# group 'Plugins' do |group|
|
52
|
+
# group.command 'plugins', 'list installed plugins'
|
53
|
+
# group.command 'plugins:install <url>', 'install the plugin from the specified git url'
|
54
|
+
# group.command 'plugins:uninstall <url/name>', 'remove the specified plugin'
|
55
|
+
# end
|
56
|
+
end
|
57
|
+
|
58
|
+
def index
|
59
|
+
display usage
|
60
|
+
end
|
61
|
+
|
62
|
+
def version
|
63
|
+
display Linguist::Client.version
|
64
|
+
end
|
65
|
+
|
66
|
+
def usage
|
67
|
+
longest_command_length = self.class.groups.map do |group|
|
68
|
+
group.map { |g| g.first.length }
|
69
|
+
end.flatten.max
|
70
|
+
|
71
|
+
self.class.groups.inject(StringIO.new) do |output, group|
|
72
|
+
output.puts "=== %s" % group.title
|
73
|
+
output.puts
|
74
|
+
|
75
|
+
group.each do |command, description|
|
76
|
+
if command.empty?
|
77
|
+
output.puts
|
78
|
+
else
|
79
|
+
output.puts "%-*s # %s" % [longest_command_length, command, description]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
output.puts
|
84
|
+
output
|
85
|
+
end.string
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
Linguist::Command::Help.create_default_groups!
|