appsendr 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +0 -0
- data/README.rdoc +50 -0
- data/Rakefile +16 -0
- data/appsendr.gemspec +41 -0
- data/bin/appsendr +14 -0
- data/lib/appsendr/binary_plist.rb +514 -0
- data/lib/appsendr/client.rb +115 -0
- data/lib/appsendr/command.rb +68 -0
- data/lib/appsendr/commands/app.rb +167 -0
- data/lib/appsendr/commands/auth.rb +155 -0
- data/lib/appsendr/commands/base.rb +63 -0
- data/lib/appsendr/commands/build.rb +198 -0
- data/lib/appsendr/commands/deploy.rb +41 -0
- data/lib/appsendr/commands/help.rb +87 -0
- data/lib/appsendr/commands/testers.rb +115 -0
- data/lib/appsendr/commands/version.rb +7 -0
- data/lib/appsendr/constants.rb +5 -0
- data/lib/appsendr/helpers.rb +120 -0
- data/lib/appsendr.rb +3 -0
- metadata +151 -0
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rest_client'
|
3
|
+
require 'uri'
|
4
|
+
require 'time'
|
5
|
+
require 'appsendr/constants'
|
6
|
+
require 'json/pure' unless {}.respond_to?(:to_json)
|
7
|
+
|
8
|
+
# A Ruby class to call the Heroku REST API. You might use this if you want to
|
9
|
+
# manage your Heroku apps from within a Ruby program, such as Capistrano.
|
10
|
+
#
|
11
|
+
# Example:
|
12
|
+
#
|
13
|
+
# require 'appsendr'
|
14
|
+
# droppr = AppSendr::Client.new('me@example.com', 'mypass')
|
15
|
+
# droppr.create('myapp')
|
16
|
+
#
|
17
|
+
class AppSendr::Client
|
18
|
+
def self.version
|
19
|
+
AppSendr::VERSION
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.gem_version_string
|
23
|
+
"appsendr-gem/#{version}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.auth(user,pass,host)
|
27
|
+
@resource = RestClient::Resource.new "http://#{host}"
|
28
|
+
resp = @resource['/api/user/authenticate'].post({:username=>user, :password=>pass})
|
29
|
+
json_resp = JSON.parse(resp)
|
30
|
+
return nil unless json_resp
|
31
|
+
return json_resp["message"] if json_resp["status"].to_i == 200
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_reader :host, :user, :password, :api_key
|
36
|
+
|
37
|
+
def initialize(api_key,host='appsendr.com')
|
38
|
+
@api_key = api_key
|
39
|
+
@host = host
|
40
|
+
@resource = RestClient::Resource.new "http://#{@host}"
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
def authenticate
|
45
|
+
post('/api/user/authenticate');
|
46
|
+
end
|
47
|
+
|
48
|
+
def list
|
49
|
+
JSON.parse(get('/api/app/list'))
|
50
|
+
end
|
51
|
+
|
52
|
+
def link(id)
|
53
|
+
post('/api/app/link',{:id=>id});
|
54
|
+
end
|
55
|
+
|
56
|
+
def create(name)
|
57
|
+
JSON.parse(post('/api/app/create', {:name=>name}))
|
58
|
+
end
|
59
|
+
|
60
|
+
def add_tester(app_id,email,name,udid=nil)
|
61
|
+
post('/api/app/add_tester/'+app_id.to_s,{:email=>email, :name=>name});
|
62
|
+
end
|
63
|
+
|
64
|
+
def remove_tester(app_id,email)
|
65
|
+
post('/api/app/remove_tester/'+app_id.to_s,{:email=>email});
|
66
|
+
end
|
67
|
+
|
68
|
+
def clear_testers(app_id)
|
69
|
+
delete('/api/app/clear_testers/'+app_id.to_s,{});
|
70
|
+
end
|
71
|
+
|
72
|
+
def testers(app_id)
|
73
|
+
JSON.parse(get('/api/app/testers/'+app_id.to_s))
|
74
|
+
end
|
75
|
+
def notify(app_id)
|
76
|
+
JSON.parse(get('/api/app/notify/'+app_id.to_s))
|
77
|
+
end
|
78
|
+
|
79
|
+
def upload(app_id, ipa,provisioning, notes, bundle_id, icon)
|
80
|
+
params = {
|
81
|
+
:ipa => File.new(ipa, 'rb'),
|
82
|
+
:profile => File.new(provisioning, 'rb'),
|
83
|
+
:notes=>notes,
|
84
|
+
:bundle_identifier => bundle_id,
|
85
|
+
:built_at=>Time.now
|
86
|
+
}
|
87
|
+
if icon
|
88
|
+
params[:icon] = File.new(icon,'rb')
|
89
|
+
end
|
90
|
+
post('/api/app/upload/'+app_id.to_s, params)
|
91
|
+
end
|
92
|
+
|
93
|
+
protected
|
94
|
+
|
95
|
+
def api_params(params={})
|
96
|
+
params[:api_key] ||= @api_key
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
def get(uri,params={})
|
101
|
+
params[:api_key] ||= @api_key
|
102
|
+
RestClient.get @resource.url+uri, {:params=>params}
|
103
|
+
#@resource[uri].get(api_params(params))#, :content_type => 'application/json')
|
104
|
+
end
|
105
|
+
def post(uri,params={})
|
106
|
+
params[:api_key] ||= @api_key
|
107
|
+
@resource[uri].post(params)#, :content_type => 'application/json')
|
108
|
+
end
|
109
|
+
|
110
|
+
def delete(uri,params={})
|
111
|
+
params[:api_key] ||= @api_key
|
112
|
+
print params
|
113
|
+
@resource[uri].delete(params)#, :content_type => 'application/json')
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'appsendr/helpers'
|
2
|
+
require 'appsendr/binary_plist'
|
3
|
+
require 'appsendr/commands/base'
|
4
|
+
|
5
|
+
Dir["#{File.dirname(__FILE__)}/commands/*.rb"].each { |c| require c }
|
6
|
+
|
7
|
+
module AppSendr
|
8
|
+
module Command
|
9
|
+
class InvalidCommand < RuntimeError; end
|
10
|
+
class CommandFailed < RuntimeError; end
|
11
|
+
|
12
|
+
extend AppSendr::Helpers
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
def run(command, args, retries=0)
|
17
|
+
begin
|
18
|
+
run_internal 'auth:reauthorize', args.dup if (retries > 0 or !credentials_setup?)
|
19
|
+
run_internal(command, args.dup)
|
20
|
+
rescue InvalidCommand
|
21
|
+
error "Unknown command. Run 'appsendr help' for usage information."
|
22
|
+
rescue RestClient::Unauthorized
|
23
|
+
if retries < 3
|
24
|
+
STDERR.puts "Authentication failure"
|
25
|
+
run(command, args, retries+1)
|
26
|
+
else
|
27
|
+
error "Authentication failure"
|
28
|
+
end
|
29
|
+
rescue RestClient::RequestFailed
|
30
|
+
error "Something went wrong with the server."
|
31
|
+
rescue CommandFailed => e
|
32
|
+
error e.message
|
33
|
+
rescue Interrupt => e
|
34
|
+
error "\n[canceled]"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def run_internal(command, args, appsendr=nil)
|
39
|
+
klass, method = parse(command)
|
40
|
+
runner = klass.new(args, appsendr)
|
41
|
+
raise InvalidCommand unless runner.respond_to?(method)
|
42
|
+
runner.send(method)
|
43
|
+
end
|
44
|
+
|
45
|
+
def parse(command)
|
46
|
+
parts = command.split(':')
|
47
|
+
case parts.size
|
48
|
+
when 1
|
49
|
+
begin
|
50
|
+
return eval("AppSendr::Command::#{command.capitalize}"), :index
|
51
|
+
rescue NameError, NoMethodError
|
52
|
+
return AppSendr::Command::App, command.to_sym
|
53
|
+
end
|
54
|
+
else
|
55
|
+
begin
|
56
|
+
const = AppSendr::Command
|
57
|
+
command = parts.pop
|
58
|
+
parts.each { |part| const = const.const_get(part.capitalize) }
|
59
|
+
return const, command.to_sym
|
60
|
+
rescue NameError
|
61
|
+
raise InvalidCommand
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'readline'
|
2
|
+
#require 'launchy'
|
3
|
+
|
4
|
+
module AppSendr::Command
|
5
|
+
class App < Base
|
6
|
+
attr_accessor :app_id, :app_name, :app
|
7
|
+
|
8
|
+
def list
|
9
|
+
list = appsendr.list
|
10
|
+
if list["message"].size > 0
|
11
|
+
display "=== Your apps"
|
12
|
+
i = 0
|
13
|
+
display list["message"].map {|app, id|
|
14
|
+
"#{i+=1}. #{app['app']['name'].ljust(35)}"
|
15
|
+
}.join("\n")
|
16
|
+
else
|
17
|
+
display "You have no apps."
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def create
|
22
|
+
# username = extract_option('--username')
|
23
|
+
# display username
|
24
|
+
# password = extract_option('--password')
|
25
|
+
# display password
|
26
|
+
#
|
27
|
+
|
28
|
+
if require_in_project_and_no_droppr(1,"an app name","a name to create an app on appsendr",true)
|
29
|
+
name = args.join(" ").strip
|
30
|
+
resp = appsendr.create(name)
|
31
|
+
make_appsendr_dir if resp['message']
|
32
|
+
@app = [resp['message']['app']['id'],resp['message']['app']['name']]
|
33
|
+
write_app
|
34
|
+
display "Your app has been created."
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def link
|
40
|
+
if in_project_dir?
|
41
|
+
error "This app is already linked to an appsendr." unless !has_project_droppr?
|
42
|
+
|
43
|
+
app = list_apps_and_ask
|
44
|
+
return unless app
|
45
|
+
|
46
|
+
link = appsendr.link(app['id'])
|
47
|
+
make_appsendr_dir if link
|
48
|
+
@app = [app['id'],app['name']]
|
49
|
+
write_app
|
50
|
+
display "Your app has been linked."
|
51
|
+
|
52
|
+
else
|
53
|
+
error("You are not in a project")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def list_apps_and_ask
|
58
|
+
apps = []
|
59
|
+
lines = []
|
60
|
+
list = appsendr.list
|
61
|
+
if list["message"].size > 0
|
62
|
+
display "=== Your apps"
|
63
|
+
i = 0
|
64
|
+
list["message"].each{|app|
|
65
|
+
apps.push({"name"=>app['app']['name'], "id"=>app['app']['id']})
|
66
|
+
lines.push("#{i+=1}. #{app['app']['name'].ljust(35)}")
|
67
|
+
}
|
68
|
+
display lines.join("\n")
|
69
|
+
print "\n"
|
70
|
+
print "Enter the # for the app you wish to link to: "
|
71
|
+
app_number = ask
|
72
|
+
|
73
|
+
return apps[app_number.to_i-1]
|
74
|
+
|
75
|
+
else
|
76
|
+
display "You have no apps."
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# def info
|
81
|
+
# name = (args.first && !args.first =~ /^\-\-/) ? args.first : extract_app
|
82
|
+
# attrs = appsendr.info(name)
|
83
|
+
#
|
84
|
+
# attrs[:web_url] ||= "http://#{attrs[:name]}.#{heroku.host}/"
|
85
|
+
# attrs[:git_url] ||= "git@#{heroku.host}:#{attrs[:name]}.git"
|
86
|
+
#
|
87
|
+
# display "=== #{attrs[:name]}"
|
88
|
+
# display "Web URL: #{attrs[:web_url]}"
|
89
|
+
# display "Domain name: http://#{attrs[:domain_name]}/" if attrs[:domain_name]
|
90
|
+
# display "Git Repo: #{attrs[:git_url]}"
|
91
|
+
# display "Dynos: #{attrs[:dynos]}"
|
92
|
+
# display "Workers: #{attrs[:workers]}"
|
93
|
+
# display "Repo size: #{format_bytes(attrs[:repo_size])}" if attrs[:repo_size]
|
94
|
+
# display "Slug size: #{format_bytes(attrs[:slug_size])}" if attrs[:slug_size]
|
95
|
+
# display "Stack: #{attrs[:stack]}" if attrs[:stack]
|
96
|
+
# if attrs[:database_size]
|
97
|
+
# data = format_bytes(attrs[:database_size])
|
98
|
+
# if tables = attrs[:database_tables]
|
99
|
+
# data = data.gsub('(empty)', '0K') + " in #{quantify("table", tables)}"
|
100
|
+
# end
|
101
|
+
# display "Data size: #{data}"
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# if attrs[:cron_next_run]
|
105
|
+
# display "Next cron: #{format_date(attrs[:cron_next_run])} (scheduled)"
|
106
|
+
# end
|
107
|
+
# if attrs[:cron_finished_at]
|
108
|
+
# display "Last cron: #{format_date(attrs[:cron_finished_at])} (finished)"
|
109
|
+
# end
|
110
|
+
#
|
111
|
+
# unless attrs[:addons].empty?
|
112
|
+
# display "Addons: " + attrs[:addons].map { |a| a['description'] }.join(', ')
|
113
|
+
# end
|
114
|
+
#
|
115
|
+
# display "Owner: #{attrs[:owner]}"
|
116
|
+
# collaborators = attrs[:collaborators].delete_if { |c| c[:email] == attrs[:owner] }
|
117
|
+
# unless collaborators.empty?
|
118
|
+
# first = true
|
119
|
+
# lead = "Collaborators:"
|
120
|
+
# attrs[:collaborators].each do |collaborator|
|
121
|
+
# display "#{first ? lead : ' ' * lead.length} #{collaborator[:email]}"
|
122
|
+
# first = false
|
123
|
+
# end
|
124
|
+
# end
|
125
|
+
#
|
126
|
+
# if attrs[:create_status] != "complete"
|
127
|
+
# display "Create Status: #{attrs[:create_status]}"
|
128
|
+
# end
|
129
|
+
# end
|
130
|
+
|
131
|
+
protected
|
132
|
+
|
133
|
+
def make_appsendr_dir
|
134
|
+
if in_project_dir?
|
135
|
+
FileUtils.mkdir_p(Dir.pwd+"/"+AppSendr::PROJECT_DIR)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def get_app
|
140
|
+
return if @app
|
141
|
+
unless @app = read_app
|
142
|
+
end
|
143
|
+
@app
|
144
|
+
end
|
145
|
+
|
146
|
+
def get_app_id
|
147
|
+
return if @app_id
|
148
|
+
unless @app_id = read_app_id
|
149
|
+
end
|
150
|
+
@app_id
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
def write_app
|
155
|
+
FileUtils.mkdir_p(File.dirname(project_appsendr_app))
|
156
|
+
File.open(project_appsendr_app, 'w') do |f|
|
157
|
+
f.puts self.app.join("\n")
|
158
|
+
end
|
159
|
+
set_app_permissions
|
160
|
+
end
|
161
|
+
|
162
|
+
def set_app_permissions
|
163
|
+
FileUtils.chmod 0700, File.dirname(project_appsendr_app)
|
164
|
+
FileUtils.chmod 0600, project_appsendr_app
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
module AppSendr::Command
|
2
|
+
class Auth < Base
|
3
|
+
attr_accessor :credentials
|
4
|
+
|
5
|
+
def client
|
6
|
+
@client ||= init_for_credentials
|
7
|
+
end
|
8
|
+
|
9
|
+
def init_for_credentials
|
10
|
+
client = AppSendr::Client.new(api_key, host)
|
11
|
+
#client.on_warning { |msg| self.display("\n#{msg}\n\n") }
|
12
|
+
client
|
13
|
+
end
|
14
|
+
|
15
|
+
def auth_credentials(user,pass)
|
16
|
+
AppSendr::Client.auth(user,pass,host)
|
17
|
+
end
|
18
|
+
|
19
|
+
# just a stub; will raise if not authenticated
|
20
|
+
def check
|
21
|
+
client.list
|
22
|
+
end
|
23
|
+
|
24
|
+
def host
|
25
|
+
ENV['APPDROPPR_HOST'] || 'appsendr.com' ##'appsendr.heroku.com' #
|
26
|
+
end
|
27
|
+
|
28
|
+
def reauthorize
|
29
|
+
user_pass = ask_for_credentials
|
30
|
+
@credentials = auth_credentials(user_pass[0],user_pass[1])
|
31
|
+
write_credentials
|
32
|
+
end
|
33
|
+
|
34
|
+
def api_key
|
35
|
+
get_credentials
|
36
|
+
@credentials[0]
|
37
|
+
end
|
38
|
+
|
39
|
+
# def user # :nodoc:
|
40
|
+
# get_credentials
|
41
|
+
# @credentials[0]
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# def password # :nodoc:
|
45
|
+
# get_credentials
|
46
|
+
# @credentials[1]
|
47
|
+
# end
|
48
|
+
|
49
|
+
def get_credentials # :nodoc:
|
50
|
+
return if @credentials
|
51
|
+
unless @credentials = read_credentials
|
52
|
+
user_pass = ask_for_credentials
|
53
|
+
@credentials = auth_credentials(user_pass[0],user_pass[1])
|
54
|
+
|
55
|
+
#@credentials = ask_for_credentials
|
56
|
+
save_credentials
|
57
|
+
end
|
58
|
+
@credentials
|
59
|
+
end
|
60
|
+
|
61
|
+
def read_credentials
|
62
|
+
File.exists?(credentials_file) and File.read(credentials_file).split("\n")
|
63
|
+
end
|
64
|
+
|
65
|
+
def echo_off
|
66
|
+
system "stty -echo"
|
67
|
+
end
|
68
|
+
|
69
|
+
def echo_on
|
70
|
+
system "stty echo"
|
71
|
+
end
|
72
|
+
|
73
|
+
def ask_for_credentials
|
74
|
+
puts "Enter your AppSendr credentials."
|
75
|
+
|
76
|
+
print "Username: "
|
77
|
+
user = ask
|
78
|
+
|
79
|
+
print "Password: "
|
80
|
+
password = running_on_windows? ? ask_for_password_on_windows : ask_for_password
|
81
|
+
|
82
|
+
[ user, password ]
|
83
|
+
end
|
84
|
+
|
85
|
+
def ask_for_password_on_windows
|
86
|
+
require "Win32API"
|
87
|
+
char = nil
|
88
|
+
password = ''
|
89
|
+
|
90
|
+
while char = Win32API.new("crtdll", "_getch", [ ], "L").Call do
|
91
|
+
break if char == 10 || char == 13 # received carriage return or newline
|
92
|
+
if char == 127 || char == 8 # backspace and delete
|
93
|
+
password.slice!(-1, 1)
|
94
|
+
else
|
95
|
+
# windows might throw a -1 at us so make sure to handle RangeError
|
96
|
+
(password << char.chr) rescue RangeError
|
97
|
+
end
|
98
|
+
end
|
99
|
+
puts
|
100
|
+
return password
|
101
|
+
end
|
102
|
+
|
103
|
+
def ask_for_password
|
104
|
+
echo_off
|
105
|
+
password = ask
|
106
|
+
puts
|
107
|
+
echo_on
|
108
|
+
return password
|
109
|
+
end
|
110
|
+
|
111
|
+
def save_credentials
|
112
|
+
begin
|
113
|
+
write_credentials
|
114
|
+
#command = args.any? { |a| a == '--ignore-keys' } ? 'auth:check' : 'keys:add'
|
115
|
+
command = 'auth:check'
|
116
|
+
AppSendr::Command.run_internal(command, args)
|
117
|
+
rescue RestClient::Unauthorized => e
|
118
|
+
delete_credentials
|
119
|
+
raise e unless retry_login?
|
120
|
+
|
121
|
+
display "\nAuthentication failed"
|
122
|
+
user_pass = ask_for_credentials
|
123
|
+
@credentials = auth_credentials(user_pass[0],user_pass[1])
|
124
|
+
@client = init_for_credentials
|
125
|
+
retry
|
126
|
+
rescue Exception => e
|
127
|
+
delete_credentials
|
128
|
+
raise e
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def retry_login?
|
133
|
+
@login_attempts ||= 0
|
134
|
+
@login_attempts += 1
|
135
|
+
@login_attempts < 3
|
136
|
+
end
|
137
|
+
|
138
|
+
def write_credentials
|
139
|
+
FileUtils.mkdir_p(File.dirname(credentials_file))
|
140
|
+
File.open(credentials_file, 'w') do |f|
|
141
|
+
f.puts self.credentials
|
142
|
+
end
|
143
|
+
set_credentials_permissions
|
144
|
+
end
|
145
|
+
|
146
|
+
def set_credentials_permissions
|
147
|
+
FileUtils.chmod 0700, File.dirname(credentials_file)
|
148
|
+
FileUtils.chmod 0600, credentials_file
|
149
|
+
end
|
150
|
+
|
151
|
+
def delete_credentials
|
152
|
+
FileUtils.rm_f(credentials_file)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module AppSendr::Command
|
4
|
+
class Base
|
5
|
+
include AppSendr::Helpers
|
6
|
+
|
7
|
+
attr_accessor :args
|
8
|
+
attr_reader :autodetected_app
|
9
|
+
def initialize(args, appsendr=nil)
|
10
|
+
@args = args
|
11
|
+
@appsendr = appsendr
|
12
|
+
@autodetected_app = false
|
13
|
+
end
|
14
|
+
|
15
|
+
def confirm(message="Are you sure you wish to continue? (y/n)?")
|
16
|
+
display("#{message} ", false)
|
17
|
+
ask.downcase == 'y'
|
18
|
+
end
|
19
|
+
|
20
|
+
def format_date(date)
|
21
|
+
date = Time.parse(date) if date.is_a?(String)
|
22
|
+
date.strftime("%Y-%m-%d %H:%M %Z")
|
23
|
+
end
|
24
|
+
|
25
|
+
def ask
|
26
|
+
gets.strip
|
27
|
+
end
|
28
|
+
|
29
|
+
def appsendr
|
30
|
+
@appsendr ||= AppSendr::Command.run_internal('auth:client', args)
|
31
|
+
end
|
32
|
+
|
33
|
+
def extract_app(force=true)
|
34
|
+
app = extract_option('--app', false)
|
35
|
+
raise(CommandFailed, "You must specify an app name after --app") if app == false
|
36
|
+
unless app
|
37
|
+
app = extract_app_in_dir(Dir.pwd) ||
|
38
|
+
raise(CommandFailed, "No app specified.\nRun this command from app folder or set it adding --app <app name>") if force
|
39
|
+
@autodetected_app = true
|
40
|
+
end
|
41
|
+
app
|
42
|
+
end
|
43
|
+
|
44
|
+
def extract_option(options, default=true)
|
45
|
+
values = options.is_a?(Array) ? options : [options]
|
46
|
+
return unless opt_index = args.select { |a| values.include? a }.first
|
47
|
+
opt_position = args.index(opt_index) + 1
|
48
|
+
if args.size > opt_position && opt_value = args[opt_position]
|
49
|
+
if opt_value.include?('--')
|
50
|
+
opt_value = nil
|
51
|
+
else
|
52
|
+
args.delete_at(opt_position)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
opt_value ||= default
|
56
|
+
args.delete(opt_index)
|
57
|
+
block_given? ? yield(opt_value) : opt_value
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
end
|