morale 0.1.0
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/Gemfile +19 -0
- data/Gemfile.lock +56 -0
- data/LICENSE +0 -0
- data/README.md +38 -0
- data/Rakefile +150 -0
- data/bin/morale +8 -0
- data/features/accounts.feature +88 -0
- data/features/login.feature +65 -0
- data/features/projects.feature +110 -0
- data/features/step_definitions/models.rb +3 -0
- data/features/support/env.rb +6 -0
- data/features/support/hooks.rb +12 -0
- data/features/tickets.feature +26 -0
- data/lib/morale/account.rb +119 -0
- data/lib/morale/authorization.rb +20 -0
- data/lib/morale/client.rb +76 -0
- data/lib/morale/command.rb +70 -0
- data/lib/morale/commands/account.rb +61 -0
- data/lib/morale/commands/authorization.rb +13 -0
- data/lib/morale/commands/project.rb +63 -0
- data/lib/morale/commands/ticket.rb +36 -0
- data/lib/morale/credentials_store.rb +41 -0
- data/lib/morale/flow.rb +17 -0
- data/lib/morale/platform.rb +64 -0
- data/lib/morale.rb +5 -0
- data/morale.gemspec +70 -0
- data/spec/morale/account_spec.rb +11 -0
- data/spec/morale/client_spec.rb +92 -0
- data/spec/morale/command_spec.rb +67 -0
- data/spec/morale/credentials_store_spec.rb +46 -0
- data/spec/spec_helper.rb +21 -0
- metadata +148 -0
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'morale/flow'
|
2
|
+
require 'morale/credentials_store'
|
3
|
+
|
4
|
+
module Morale
|
5
|
+
class Account
|
6
|
+
include Morale::Platform
|
7
|
+
|
8
|
+
class << self
|
9
|
+
include Morale::CredentialsStore
|
10
|
+
include Morale::Flow
|
11
|
+
|
12
|
+
def subdomain
|
13
|
+
if @subdomain.nil?
|
14
|
+
get_credentials
|
15
|
+
@subdomain = @credentials[0]
|
16
|
+
end
|
17
|
+
@subdomain
|
18
|
+
end
|
19
|
+
|
20
|
+
def subdomain=(value)
|
21
|
+
@subdomain = value
|
22
|
+
@credentials ||= []
|
23
|
+
@credentials[0] = value
|
24
|
+
write_credentials
|
25
|
+
end
|
26
|
+
|
27
|
+
def api_key
|
28
|
+
get_credentials
|
29
|
+
@credentials[1]
|
30
|
+
end
|
31
|
+
|
32
|
+
def project
|
33
|
+
@credentials[2] if !@credentials.nil? && @credentials.length > 2
|
34
|
+
end
|
35
|
+
|
36
|
+
def project=(value)
|
37
|
+
@credentials ||= Array.new(3)
|
38
|
+
@credentials[2] = value
|
39
|
+
write_credentials
|
40
|
+
end
|
41
|
+
|
42
|
+
def retry_login?
|
43
|
+
@login_attempts ||= 0
|
44
|
+
@login_attempts += 1
|
45
|
+
@login_attempts < 3
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_credentials
|
49
|
+
return if @credentials
|
50
|
+
unless @credentials = read_credentials
|
51
|
+
ask_for_and_save_credentials
|
52
|
+
end
|
53
|
+
@credentials
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def ask_for_and_save_credentials
|
59
|
+
@credentials = ask_for_credentials
|
60
|
+
write_credentials
|
61
|
+
end
|
62
|
+
|
63
|
+
def ask_for_credentials
|
64
|
+
user ||= nil
|
65
|
+
|
66
|
+
begin
|
67
|
+
user = ask_for_subdomain if @subdomain.nil?
|
68
|
+
rescue Morale::Client::NotFound
|
69
|
+
say "Email is not registered."
|
70
|
+
return
|
71
|
+
end
|
72
|
+
|
73
|
+
say "Sign in to Morale."
|
74
|
+
|
75
|
+
if user.nil?
|
76
|
+
say "Email: "
|
77
|
+
user = ask
|
78
|
+
end
|
79
|
+
|
80
|
+
say "Password: "
|
81
|
+
password = running_on_windows? ? ask_for_secret_on_windows : ask_for_secret
|
82
|
+
api_key = Morale::Client.authorize(user, password, @subdomain).api_key
|
83
|
+
|
84
|
+
say "Invalid email/password combination or API key was not generated." if api_key.nil?
|
85
|
+
|
86
|
+
[@subdomain, api_key]
|
87
|
+
end
|
88
|
+
|
89
|
+
def ask_for_subdomain
|
90
|
+
say "No account specified for Morale."
|
91
|
+
|
92
|
+
say "Email: "
|
93
|
+
user = ask
|
94
|
+
|
95
|
+
accounts = Morale::Client.accounts user
|
96
|
+
account = nil
|
97
|
+
|
98
|
+
#TODO: This is the same as the account.list --change
|
99
|
+
retryable(:indefinate => true) do
|
100
|
+
accounts.sort{|a,b| a['account']['group_name'] <=> b['account']['group_name']}.each_with_index do |record, i|
|
101
|
+
say "#{i += 1}. #{record['account']['group_name']}"
|
102
|
+
end
|
103
|
+
|
104
|
+
say "Choose an account: "
|
105
|
+
index = ask
|
106
|
+
account = accounts[index.to_i - 1]
|
107
|
+
|
108
|
+
if account.nil?
|
109
|
+
say "Invalid account."
|
110
|
+
raise Exception
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
@subdomain = account['account']['site_address'] unless account.nil?
|
115
|
+
user
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'morale/account'
|
2
|
+
|
3
|
+
module Morale
|
4
|
+
class Authorization
|
5
|
+
class << self
|
6
|
+
def client
|
7
|
+
Morale::Client.new(Morale::Account.subdomain, Morale::Account.api_key)
|
8
|
+
end
|
9
|
+
|
10
|
+
def login
|
11
|
+
Morale::Account.delete_credentials
|
12
|
+
Morale::Account.get_credentials
|
13
|
+
end
|
14
|
+
|
15
|
+
def retry_login?
|
16
|
+
Morale::Account.retry_login?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
module Morale
|
5
|
+
class Client
|
6
|
+
class Unauthorized < RuntimeError; end
|
7
|
+
class NotFound < RuntimeError; end
|
8
|
+
|
9
|
+
include HTTParty
|
10
|
+
format :json
|
11
|
+
|
12
|
+
API_VERSION = 'v1'
|
13
|
+
|
14
|
+
attr_accessor :api_key
|
15
|
+
attr_reader :subdomain
|
16
|
+
|
17
|
+
def self.authorize(user, password, subdomain)
|
18
|
+
client = new(subdomain)
|
19
|
+
client.unauthorize
|
20
|
+
client.api_key = client.class.post('/in', :body => { :email => user, :password => password })["api_key"]
|
21
|
+
return client
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.accounts(user)
|
25
|
+
client = new
|
26
|
+
client.unauthorize
|
27
|
+
response = client.class.get("/accounts", :query => { :email => user })
|
28
|
+
raise Unauthorized if response.code == 401
|
29
|
+
raise NotFound if response.code == 404
|
30
|
+
response
|
31
|
+
end
|
32
|
+
|
33
|
+
def accounts
|
34
|
+
authorize
|
35
|
+
response = self.class.get("/accounts", :query => { :api_key => @api_key })
|
36
|
+
raise Unauthorized if response.code == 401
|
37
|
+
raise NotFound if response.code == 404
|
38
|
+
response
|
39
|
+
end
|
40
|
+
|
41
|
+
def initialize(subdomain="", api_key="")
|
42
|
+
@api_key = api_key
|
43
|
+
@subdomain = subdomain
|
44
|
+
#TODO: Save the domain in a config file
|
45
|
+
self.class.default_options[:base_uri] = HTTParty.normalize_base_uri("#{subdomain}#{"." unless subdomain.nil? || subdomain.empty?}lvh.me:3000/api/#{API_VERSION}")
|
46
|
+
end
|
47
|
+
|
48
|
+
def projects
|
49
|
+
authorize
|
50
|
+
response = self.class.get('/projects')
|
51
|
+
raise Unauthorized if response.code == 401
|
52
|
+
raise NotFound if response.code == 404
|
53
|
+
response
|
54
|
+
end
|
55
|
+
|
56
|
+
def tickets(options={})
|
57
|
+
end
|
58
|
+
|
59
|
+
def ticket(project_id, command)
|
60
|
+
authorize
|
61
|
+
response = self.class.post("/projects/#{project_id}/tickets", :body => { :command => command })
|
62
|
+
raise Unauthorized if response.code == 401
|
63
|
+
raise NotFound if response.code == 404
|
64
|
+
response
|
65
|
+
end
|
66
|
+
|
67
|
+
def authorize
|
68
|
+
self.class.basic_auth @subdomain, @api_key
|
69
|
+
end
|
70
|
+
|
71
|
+
def unauthorize
|
72
|
+
self.class.basic_auth nil, nil
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'thor'
|
3
|
+
require 'morale/commands/account'
|
4
|
+
require 'morale/commands/authorization'
|
5
|
+
require 'morale/commands/project'
|
6
|
+
require 'morale/commands/ticket'
|
7
|
+
|
8
|
+
module Morale
|
9
|
+
class Command < Thor
|
10
|
+
include Morale::Platform
|
11
|
+
|
12
|
+
desc "login", "Signs a user in using their email address and password. Stores the users API key locally to use for access later."
|
13
|
+
def login
|
14
|
+
Morale::Commands::Authorization.login
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "accounts [EMAIL]", "Gets all the subdomains available for a given email address if provided, else it uses the current api key."
|
18
|
+
method_options :change => false
|
19
|
+
def accounts(email="")
|
20
|
+
Morale::Commands::Account.list email, options.change
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "accounts ID", "Changes the current account to the numeric identifier of the account specified."
|
24
|
+
def account(id)
|
25
|
+
Morale::Commands::Account.select id
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "projects", "Lists the projects available to the user and the current account."
|
29
|
+
method_options :change => false
|
30
|
+
def projects
|
31
|
+
Morale::Commands::Project.list options.change
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "projects ID", "Changes the current project to the numeric identifier of the project specified."
|
35
|
+
def project(id)
|
36
|
+
Morale::Commands::Project.select id
|
37
|
+
end
|
38
|
+
|
39
|
+
desc "ticket COMMAND", "Creates, updates, or deletes a ticket based on the command specified."
|
40
|
+
method_option :command, :aliases => "-c", :type => :array, :desc => "Specify -c without putting the parameter in a string"
|
41
|
+
def ticket(command="")
|
42
|
+
if command.empty? && !options[:command].nil?
|
43
|
+
command = options[:command].compact.join(" ")
|
44
|
+
end
|
45
|
+
Morale::Commands::Ticket.command command
|
46
|
+
end
|
47
|
+
|
48
|
+
desc "test", "Testing the ticket output."
|
49
|
+
def test
|
50
|
+
say "+-----+------+-----------------------------------------------------------------------------------------+----------------+----------+----------------+----------+"
|
51
|
+
say "| id | type | title | created by | due date | assigned to | priority |"
|
52
|
+
say "+-----+------+-----------------------------------------------------------------------------------------+----------------+----------+----------------+----------+"
|
53
|
+
say "| 123 | task | hbsdbjhsdjhfdsjfhsdjfhsdjfhsdjfhsdf | Jimmy P. | 08/08/11 | Robert P. | 1 |"
|
54
|
+
say "+-----+------+-----------------------------------------------------------------------------------------+----------------+----------+----------------+----------+"
|
55
|
+
end
|
56
|
+
|
57
|
+
class << self
|
58
|
+
def client
|
59
|
+
Morale::Authorization.client
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
no_tasks do
|
64
|
+
def self.handle_no_task_error(task, has_namespace = $thor_runner) #:nodoc:
|
65
|
+
self.new.ticket ARGV.compact.join(" ")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'morale/account'
|
2
|
+
require 'morale/client'
|
3
|
+
require 'morale/command'
|
4
|
+
require 'morale/authorization'
|
5
|
+
|
6
|
+
module Morale::Commands
|
7
|
+
class Account
|
8
|
+
class << self
|
9
|
+
include Morale::Platform
|
10
|
+
|
11
|
+
def list(email="", change=false)
|
12
|
+
accounts = Morale::Client.accounts(email) unless email.nil? || email.empty?
|
13
|
+
|
14
|
+
begin
|
15
|
+
accounts = Morale::Command.client.accounts if email.nil? || email.empty?
|
16
|
+
|
17
|
+
if !accounts.nil?
|
18
|
+
accounts.sort{|a,b| a['account']['group_name'] <=> b['account']['group_name']}.each_with_index do |record, i|
|
19
|
+
say "#{i += 1}. #{record['account']['group_name']}"
|
20
|
+
end
|
21
|
+
|
22
|
+
if change
|
23
|
+
say "Choose an account: "
|
24
|
+
index = ask
|
25
|
+
account = accounts[index.to_i - 1]
|
26
|
+
|
27
|
+
if account.nil?
|
28
|
+
say "Invalid account."
|
29
|
+
end
|
30
|
+
Morale::Account.subdomain = account['account']['site_address'] unless account.nil?
|
31
|
+
end
|
32
|
+
else
|
33
|
+
say "There were no accounts found."
|
34
|
+
end
|
35
|
+
rescue Morale::Client::Unauthorized, Morale::Client::NotFound
|
36
|
+
say "Authentication failure"
|
37
|
+
Morale::Commands::Authorization.login
|
38
|
+
retry if Morale::Authorization.retry_login? && !change
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def select(id)
|
43
|
+
begin
|
44
|
+
accounts = Morale::Command.client.accounts
|
45
|
+
if !accounts.nil?
|
46
|
+
account = accounts[id.to_i - 1]
|
47
|
+
if account.nil?
|
48
|
+
say "Invalid account."
|
49
|
+
end
|
50
|
+
Morale::Account.subdomain = account['account']['site_address'] unless account.nil?
|
51
|
+
else
|
52
|
+
say "There were no accounts found."
|
53
|
+
end
|
54
|
+
rescue Morale::Client::Unauthorized, Morale::Client::NotFound
|
55
|
+
say "Authentication failure"
|
56
|
+
Morale::Commands::Authorization.login
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'morale/client'
|
2
|
+
require 'morale/command'
|
3
|
+
require 'morale/authorization'
|
4
|
+
|
5
|
+
module Morale::Commands
|
6
|
+
class Project
|
7
|
+
class << self
|
8
|
+
include Morale::Platform
|
9
|
+
|
10
|
+
def list(change=false)
|
11
|
+
begin
|
12
|
+
projects = Morale::Command.client.projects
|
13
|
+
if !projects.nil?
|
14
|
+
projects.sort{|a,b| a['project']['name'] <=> b['project']['name']}.each_with_index do |record, i|
|
15
|
+
puts "#{i += 1}. #{record['project']['name']}"
|
16
|
+
end
|
17
|
+
|
18
|
+
if change
|
19
|
+
say "Choose a project: "
|
20
|
+
index = ask
|
21
|
+
project = projects[index.to_i - 1]
|
22
|
+
|
23
|
+
if project.nil?
|
24
|
+
say "Invalid project."
|
25
|
+
end
|
26
|
+
Morale::Account.project = project['project']['id'] unless project.nil?
|
27
|
+
end
|
28
|
+
else
|
29
|
+
say "There were no projects found."
|
30
|
+
end
|
31
|
+
rescue Morale::Client::Unauthorized
|
32
|
+
say "Authentication failure"
|
33
|
+
Morale::Commands::Authorization.login
|
34
|
+
retry if Morale::Authorization.retry_login?
|
35
|
+
rescue Morale::Client::NotFound
|
36
|
+
say "Communication failure"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def select(id)
|
41
|
+
begin
|
42
|
+
projects = Morale::Command.client.projects
|
43
|
+
if !projects.nil?
|
44
|
+
project = projects[id.to_i - 1]
|
45
|
+
if project.nil?
|
46
|
+
say "Invalid project."
|
47
|
+
end
|
48
|
+
Morale::Account.project = project['project']['id'] unless project.nil?
|
49
|
+
else
|
50
|
+
say "There were no projects found."
|
51
|
+
end
|
52
|
+
rescue Morale::Client::Unauthorized
|
53
|
+
say "Authentication failure"
|
54
|
+
Morale::Commands::Authorization.login
|
55
|
+
retry if Morale::Authorization.retry_login?
|
56
|
+
rescue Morale::Client::NotFound
|
57
|
+
say "Communication failure"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'morale/client'
|
2
|
+
require 'morale/command'
|
3
|
+
require 'morale/authorization'
|
4
|
+
require 'hirb'
|
5
|
+
|
6
|
+
module Morale::Commands
|
7
|
+
class Ticket
|
8
|
+
class << self
|
9
|
+
include Morale::Platform
|
10
|
+
|
11
|
+
def command(command)
|
12
|
+
ticket = Morale::Command.client.ticket Morale::Account.project, command
|
13
|
+
print ticket
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def print(ticket)
|
19
|
+
due_date = Date.parse(ticket['due_date']).strftime("%b. %d") unless ticket['due_date'].nil?
|
20
|
+
assigned_to = "#{ticket['assigned_to']['user']['first_name']} #{(ticket['assigned_to']['user']['last_name']).to_s[0,1]}." unless ticket['assigned_to'].nil?
|
21
|
+
|
22
|
+
say Hirb::Helpers::Table.render [{
|
23
|
+
:id => ticket['identifier'],
|
24
|
+
:type => ticket['type'],
|
25
|
+
:title => ticket['title'],
|
26
|
+
:created_by => "#{ticket['created_by']['user']['first_name']} #{(ticket['created_by']['user']['last_name']).to_s[0,1]}.",
|
27
|
+
:due_date => due_date,
|
28
|
+
:assigned_to => assigned_to,
|
29
|
+
:priority => ticket['priority']
|
30
|
+
}],
|
31
|
+
:fields => [:id, :type, :title, :created_by, :due_date, :assigned_to, :priority],
|
32
|
+
:headers => { :created_by => "created by", :due_date => "due date", :assigned_to => "assigned to" }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'morale/platform'
|
2
|
+
|
3
|
+
module Morale
|
4
|
+
module CredentialsStore
|
5
|
+
include Morale::Platform
|
6
|
+
|
7
|
+
attr_accessor :credentials
|
8
|
+
|
9
|
+
def location
|
10
|
+
ENV['CREDENTIALS_LOCATION'] || default_location
|
11
|
+
end
|
12
|
+
|
13
|
+
def default_location
|
14
|
+
"#{home_directory}/.morale/credentials"
|
15
|
+
end
|
16
|
+
|
17
|
+
def read_credentials
|
18
|
+
File.exists?(location) and File.read(location).split("\n")
|
19
|
+
end
|
20
|
+
|
21
|
+
def write_credentials
|
22
|
+
FileUtils.mkdir_p(File.dirname(location))
|
23
|
+
f = File.open(location, 'w')
|
24
|
+
f.puts self.credentials
|
25
|
+
f.close
|
26
|
+
set_credentials_permissions
|
27
|
+
end
|
28
|
+
|
29
|
+
def delete_credentials
|
30
|
+
FileUtils.rm_f(location)
|
31
|
+
@credentials = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def set_credentials_permissions
|
37
|
+
FileUtils.chmod 0700, File.dirname(location)
|
38
|
+
FileUtils.chmod 0600, location
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/morale/flow.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module Morale
|
2
|
+
module Flow
|
3
|
+
def retryable(options = {}, &block)
|
4
|
+
opts = { :tries => 1, :indefinate => false, :on => Exception }.merge(options)
|
5
|
+
|
6
|
+
retry_exception, retries, indefinately = opts[:on], opts[:tries], opts[:indefinate]
|
7
|
+
|
8
|
+
begin
|
9
|
+
return yield
|
10
|
+
rescue retry_exception
|
11
|
+
retry if indefinately || (retries -= 1) > 0
|
12
|
+
end
|
13
|
+
|
14
|
+
yield
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Morale
|
2
|
+
module Platform
|
3
|
+
def home_directory
|
4
|
+
running_on_windows? ? ENV['USERPROFILE'] : ENV['HOME']
|
5
|
+
end
|
6
|
+
|
7
|
+
def running_on_windows?
|
8
|
+
RUBY_PLATFORM =~ /mswin32|mingw32/
|
9
|
+
end
|
10
|
+
|
11
|
+
def say(message="", color=nil, force_new_line=(message.to_s !~ /( |\t)$/))
|
12
|
+
message = message.to_s
|
13
|
+
message = set_color(message, color) if color
|
14
|
+
|
15
|
+
spaces = ""
|
16
|
+
|
17
|
+
if force_new_line
|
18
|
+
$stdout.puts(spaces + message)
|
19
|
+
else
|
20
|
+
$stdout.print(spaces + message)
|
21
|
+
end
|
22
|
+
$stdout.flush
|
23
|
+
end
|
24
|
+
|
25
|
+
def ask
|
26
|
+
input = $stdin.gets
|
27
|
+
input.strip! unless input.nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
def ask_for_secret_on_windows
|
31
|
+
require "Win32API"
|
32
|
+
char = nil
|
33
|
+
secret = ''
|
34
|
+
|
35
|
+
while char = Win32API.new("crtdll", "_getch", [ ], "L").Call do
|
36
|
+
break if char == 10 || char == 13 # received carriage return or newline
|
37
|
+
if char == 127 || char == 8 # backspace and delete
|
38
|
+
secret.slice!(-1, 1)
|
39
|
+
else
|
40
|
+
# windows might throw a -1 at us so make sure to handle RangeError
|
41
|
+
(secret << char.chr) rescue RangeError
|
42
|
+
end
|
43
|
+
end
|
44
|
+
puts
|
45
|
+
return secret
|
46
|
+
end
|
47
|
+
|
48
|
+
def ask_for_secret
|
49
|
+
echo_off
|
50
|
+
secret = ask
|
51
|
+
puts
|
52
|
+
echo_on
|
53
|
+
return secret
|
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
|
+
end
|
64
|
+
end
|
data/lib/morale.rb
ADDED
data/morale.gemspec
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
s.specification_version = 2 if s.respond_to? :specification_version=
|
4
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
5
|
+
s.rubygems_version = '1.3.5'
|
6
|
+
|
7
|
+
s.name = 'morale'
|
8
|
+
s.version = '0.1.0'
|
9
|
+
s.date = '2011-09-29'
|
10
|
+
s.rubyforge_project = 'morale'
|
11
|
+
|
12
|
+
s.summary = "Command line interface to create & manage tickets on Morale."
|
13
|
+
s.description = "Client library and command-line tool to manage tickets and control your account on Morale."
|
14
|
+
|
15
|
+
s.authors = ["Brilliant Fantastic"]
|
16
|
+
s.email = 'support@teammorale.com'
|
17
|
+
s.homepage = 'http://teammorale.com'
|
18
|
+
|
19
|
+
s.require_paths = %w[lib]
|
20
|
+
s.executables = ["morale"]
|
21
|
+
|
22
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
23
|
+
s.extra_rdoc_files = %w[README.md LICENSE]
|
24
|
+
|
25
|
+
s.add_dependency('hirb', "~> 0.5.0")
|
26
|
+
s.add_dependency('httparty', "~> 0.7.8")
|
27
|
+
s.add_dependency('json', "~> 1.4.6")
|
28
|
+
s.add_dependency('thor', "~> 0.14.6")
|
29
|
+
|
30
|
+
#s.add_development_dependency('DEVDEPNAME', [">= 1.1.0", "< 2.0.0"])
|
31
|
+
|
32
|
+
## Leave this section as-is. It will be automatically generated from the
|
33
|
+
## contents of your Git repository via the gemspec task. DO NOT REMOVE
|
34
|
+
## THE MANIFEST COMMENTS, they are used as delimiters by the task.
|
35
|
+
# = MANIFEST =
|
36
|
+
s.files = %w[
|
37
|
+
Gemfile
|
38
|
+
Gemfile.lock
|
39
|
+
LICENSE
|
40
|
+
README.md
|
41
|
+
Rakefile
|
42
|
+
bin/morale
|
43
|
+
features/accounts.feature
|
44
|
+
features/login.feature
|
45
|
+
features/projects.feature
|
46
|
+
features/step_definitions/models.rb
|
47
|
+
features/support/env.rb
|
48
|
+
features/support/hooks.rb
|
49
|
+
features/tickets.feature
|
50
|
+
lib/morale.rb
|
51
|
+
lib/morale/account.rb
|
52
|
+
lib/morale/authorization.rb
|
53
|
+
lib/morale/client.rb
|
54
|
+
lib/morale/command.rb
|
55
|
+
lib/morale/commands/account.rb
|
56
|
+
lib/morale/commands/authorization.rb
|
57
|
+
lib/morale/commands/project.rb
|
58
|
+
lib/morale/commands/ticket.rb
|
59
|
+
lib/morale/credentials_store.rb
|
60
|
+
lib/morale/flow.rb
|
61
|
+
lib/morale/platform.rb
|
62
|
+
morale.gemspec
|
63
|
+
spec/morale/account_spec.rb
|
64
|
+
spec/morale/client_spec.rb
|
65
|
+
spec/morale/command_spec.rb
|
66
|
+
spec/morale/credentials_store_spec.rb
|
67
|
+
spec/spec_helper.rb
|
68
|
+
]
|
69
|
+
# = MANIFEST =
|
70
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'morale/account'
|
3
|
+
|
4
|
+
describe Morale::Account do
|
5
|
+
describe "#subdomain" do
|
6
|
+
it "should store the subdomain in the credentials file" do
|
7
|
+
Morale::Account.subdomain = "blah"
|
8
|
+
File.read(Morale::Account.location).should =~ /blah/
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|