morale 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|