hoodie 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +4 -0
- data/LICENSE +201 -0
- data/README.md +1 -0
- data/Rakefile +14 -0
- data/hoodie.gemspec +30 -0
- data/lib/hoodie.rb +31 -0
- data/lib/hoodie/blank.rb +123 -0
- data/lib/hoodie/file.rb +69 -0
- data/lib/hoodie/hash.rb +167 -0
- data/lib/hoodie/memoizable.rb +44 -0
- data/lib/hoodie/obfuscate.rb +121 -0
- data/lib/hoodie/os.rb +43 -0
- data/lib/hoodie/path_finder.rb +75 -0
- data/lib/hoodie/stash.rb +118 -0
- data/lib/hoodie/stash/disk_store.rb +151 -0
- data/lib/hoodie/stash/mem_store.rb +150 -0
- data/lib/hoodie/utils.rb +134 -0
- data/lib/hoodie/version.rb +22 -0
- metadata +161 -0
data/lib/hoodie/stash.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Author: Stefano Harding <riddopic@gmail.com>
|
4
|
+
#
|
5
|
+
# Copyright (C) 2014 Stefano Harding
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'hoodie/stash/disk_store' unless defined?(DiskStash)
|
21
|
+
require 'hoodie/stash/mem_store' unless defined?(MemStash)
|
22
|
+
|
23
|
+
# Define the basic cache and default store objects
|
24
|
+
module Stash
|
25
|
+
|
26
|
+
# check if we're using a version if Ruby that supports caller_locations
|
27
|
+
NEW_CALL = Kernel.respond_to? 'caller_locations'
|
28
|
+
|
29
|
+
class << self
|
30
|
+
# insert a helper .new() method for creating a new object
|
31
|
+
#
|
32
|
+
def new(*args)
|
33
|
+
self::Cache.new(*args)
|
34
|
+
end
|
35
|
+
|
36
|
+
# helper to get the calling function name
|
37
|
+
#
|
38
|
+
def caller_name
|
39
|
+
NEW_CALL ? caller_locations(2, 1).first.label : caller[1][/`([^']*)'/, 1]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Default store type
|
44
|
+
DEFAULT_STORE = MemStash::Cache
|
45
|
+
|
46
|
+
# Key/value cache store
|
47
|
+
class Cache
|
48
|
+
# @return [Hash] of the mem stash cache hash store
|
49
|
+
#
|
50
|
+
attr_reader :store
|
51
|
+
|
52
|
+
# Initializes a new empty store
|
53
|
+
#
|
54
|
+
def initialize(params = {})
|
55
|
+
params = { store: params } unless params.is_a? Hash
|
56
|
+
@store = params.fetch(:store) { Stash::DEFAULT_STORE.new }
|
57
|
+
end
|
58
|
+
|
59
|
+
# Clear the whole stash store or the value of a key
|
60
|
+
#
|
61
|
+
# @param key [Symbol, String] (optional) representing the key to
|
62
|
+
# clear.
|
63
|
+
#
|
64
|
+
# @return nothing.
|
65
|
+
#
|
66
|
+
def clear!(key = nil)
|
67
|
+
key = key.to_sym unless key.nil?
|
68
|
+
@store.clear! key
|
69
|
+
end
|
70
|
+
|
71
|
+
# Retrieves the value for a given key, if nothing is set,
|
72
|
+
# returns KeyError
|
73
|
+
#
|
74
|
+
# @param key [Symbol, String] representing the key
|
75
|
+
#
|
76
|
+
# @raise [KeyError] if no such key found
|
77
|
+
#
|
78
|
+
# @return [Hash, Array, String] value for key
|
79
|
+
#
|
80
|
+
def [](key = nil)
|
81
|
+
key ||= Stash.caller_name
|
82
|
+
fail KeyError, 'Key not cached' unless include? key.to_sym
|
83
|
+
@store[key.to_sym]
|
84
|
+
end
|
85
|
+
|
86
|
+
# Retrieves the value for a given key, if nothing is set,
|
87
|
+
# run the code, cache the result, and return it
|
88
|
+
#
|
89
|
+
# @param key [Symbol, String] representing the key
|
90
|
+
# @param block [&block] that returns the value to set (optional)
|
91
|
+
#
|
92
|
+
# @return [Hash, Array, String] value for key
|
93
|
+
#
|
94
|
+
def cache(key = nil, &code)
|
95
|
+
key ||= Stash.caller_name
|
96
|
+
@store[key.to_sym] ||= code.call
|
97
|
+
end
|
98
|
+
|
99
|
+
# return the size of the store as an integer
|
100
|
+
#
|
101
|
+
# @return [Fixnum]
|
102
|
+
#
|
103
|
+
def size
|
104
|
+
@store.size
|
105
|
+
end
|
106
|
+
|
107
|
+
# return a boolean indicating presence of the given key in the store
|
108
|
+
#
|
109
|
+
# @param key [Symbol, String] a string or symbol representing the key
|
110
|
+
#
|
111
|
+
# @return [TrueClass, FalseClass]
|
112
|
+
#
|
113
|
+
def include?(key = nil)
|
114
|
+
key ||= Stash.caller_name
|
115
|
+
@store.include? key.to_sym
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Author: Stefano Harding <riddopic@gmail.com>
|
4
|
+
#
|
5
|
+
# Copyright (C) 2014 Stefano Harding
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'tmpdir'
|
21
|
+
|
22
|
+
module DiskStash
|
23
|
+
# Disk stashing method variable caching hash, string, array store.
|
24
|
+
class Cache
|
25
|
+
include Enumerable
|
26
|
+
|
27
|
+
# @return [String] location of DiskStash::Cache.store
|
28
|
+
#
|
29
|
+
attr_reader :store
|
30
|
+
|
31
|
+
# Initializes a new disked backed stash hash cache store.
|
32
|
+
#
|
33
|
+
# @param path [String] location for stash store cache.
|
34
|
+
#
|
35
|
+
# @return nothing.
|
36
|
+
#
|
37
|
+
def initialize(store = file_store)
|
38
|
+
@store = store
|
39
|
+
ensure_store_directory
|
40
|
+
end
|
41
|
+
|
42
|
+
# Clear the whole stash or the value of a key
|
43
|
+
#
|
44
|
+
# @param key [Symbol, String] (optional) string or symbol
|
45
|
+
# representing the key to clear
|
46
|
+
#
|
47
|
+
# @return [Hash] with a key, return the value it had, without
|
48
|
+
# returns {}
|
49
|
+
#
|
50
|
+
def clear!(key = nil)
|
51
|
+
if key.nil?
|
52
|
+
::Dir[::File.join(store, '*.cache')].each do |file|
|
53
|
+
::File.delete(file)
|
54
|
+
end
|
55
|
+
else
|
56
|
+
::File.delete(cache_file(key)) if ::File.exists?(cache_file(key))
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Retrieves the value for a given key, if nothing is set,
|
61
|
+
# returns KeyError
|
62
|
+
#
|
63
|
+
# @param key [Symbol, String] representing the key
|
64
|
+
#
|
65
|
+
# @raise [KeyError] if no such key found
|
66
|
+
#
|
67
|
+
# @return [Hash, Array, String] value for key
|
68
|
+
#
|
69
|
+
def [](key)
|
70
|
+
if key.is_a? Array
|
71
|
+
hash = {}
|
72
|
+
key.each do |k|
|
73
|
+
hash[k] = Marshal::load(read_cache_file(k))
|
74
|
+
end
|
75
|
+
hash unless hash.empty?
|
76
|
+
else
|
77
|
+
Marshal::load(read_cache_file(key))
|
78
|
+
end
|
79
|
+
rescue Errno::ENOENT
|
80
|
+
nil # key hasn't been created
|
81
|
+
end
|
82
|
+
|
83
|
+
# Store the given value with the given key, either an an argument
|
84
|
+
# or block. If a previous value was set it will be overwritten
|
85
|
+
# with the new value.
|
86
|
+
# #
|
87
|
+
# @param key [Symbol, String] representing the key
|
88
|
+
# @param value [Object] that represents the value (optional)
|
89
|
+
# @param block [&block] that returns the value to set (optional)
|
90
|
+
#
|
91
|
+
# @return nothing.
|
92
|
+
#
|
93
|
+
def []=(key, value)
|
94
|
+
write_cache_file(key, Marshal::dump(value))
|
95
|
+
end
|
96
|
+
|
97
|
+
# returns path to cache file with 'key'
|
98
|
+
def cache_file key
|
99
|
+
::File.join(store, key.to_s + '.cache')
|
100
|
+
end
|
101
|
+
|
102
|
+
private # P R O P R I E T À P R I V A T A divieto di accesso
|
103
|
+
|
104
|
+
# return Chef tmpfile path if running under Chef, else return OS
|
105
|
+
# temp path. On Winders Dir.tmpdir returns the correct path.
|
106
|
+
#
|
107
|
+
def file_store
|
108
|
+
tmp = defined?(Chef::Config) ? Chef::Config[:file_cache_path] : Dir.tmpdir
|
109
|
+
if OS.windows?
|
110
|
+
win_friendly_path(::File.join(tmp, '._stash'))
|
111
|
+
else
|
112
|
+
'_stash'
|
113
|
+
# ::File.join(tmp, '._stash')
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# returns windows friendly version of the provided path, ensures
|
118
|
+
# backslashes are used everywhere
|
119
|
+
#
|
120
|
+
def win_friendly_path(path)
|
121
|
+
system_drive = ENV['SYSTEMDRIVE'] ? ENV['SYSTEMDRIVE'] : ""
|
122
|
+
path = ::File.join(system_drive, path)
|
123
|
+
path.gsub!(::File::SEPARATOR, (::File::ALT_SEPARATOR || '\\'))
|
124
|
+
end
|
125
|
+
|
126
|
+
def write_cache_file(key, content)
|
127
|
+
f = ::File.open(cache_file(key), 'w+' )
|
128
|
+
f.flock(::File::LOCK_EX)
|
129
|
+
f.write(content)
|
130
|
+
f.close
|
131
|
+
content
|
132
|
+
end
|
133
|
+
|
134
|
+
def read_cache_file(key)
|
135
|
+
f = ::File.open(cache_file(key), 'r')
|
136
|
+
f.flock(::File::LOCK_SH)
|
137
|
+
out = f.read
|
138
|
+
f.close
|
139
|
+
out
|
140
|
+
end
|
141
|
+
|
142
|
+
def read_cache_mtime(key)
|
143
|
+
nil unless ::File.exists?(cache_file(key))
|
144
|
+
::File.mtime(cache_file(key))
|
145
|
+
end
|
146
|
+
|
147
|
+
def ensure_store_directory
|
148
|
+
::Dir.mkdir(store) unless ::File.directory?(store)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Author: Stefano Harding <riddopic@gmail.com>
|
4
|
+
#
|
5
|
+
# Copyright (C) 2014 Stefano Harding
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
module MemStash
|
21
|
+
# Basic cache object stash store (uses a Hash)
|
22
|
+
class Cache
|
23
|
+
include Enumerable
|
24
|
+
|
25
|
+
# @return [Hash] of the mem stash cache hash store
|
26
|
+
#
|
27
|
+
attr_reader :store
|
28
|
+
|
29
|
+
# Initializes a new store object.
|
30
|
+
#
|
31
|
+
# @param data [Hash] (optional) data to load into the stash.
|
32
|
+
#
|
33
|
+
# @return nothing.
|
34
|
+
#
|
35
|
+
def initialize(_ = {})
|
36
|
+
@store = {}
|
37
|
+
end
|
38
|
+
|
39
|
+
# Clear the whole stash store or the value of a key
|
40
|
+
#
|
41
|
+
# @param key [Symbol, String] (optional) representing the key to
|
42
|
+
# clear.
|
43
|
+
#
|
44
|
+
# @return nothing.
|
45
|
+
#
|
46
|
+
def clear!(key = nil)
|
47
|
+
key.nil? ? @store.clear : @store.delete(key)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Retrieves the value for a given key, if nothing is set,
|
51
|
+
# returns KeyError
|
52
|
+
#
|
53
|
+
# @param key [Symbol, String] representing the key
|
54
|
+
#
|
55
|
+
# @raise [KeyError] if no such key found
|
56
|
+
#
|
57
|
+
# @return [Hash, Array, String] value for key
|
58
|
+
#
|
59
|
+
def [](key)
|
60
|
+
@store[key]
|
61
|
+
end
|
62
|
+
|
63
|
+
# Store the given value with the given key, either an an argument
|
64
|
+
# or block. If a previous value was set it will be overwritten
|
65
|
+
# with the new value.
|
66
|
+
#
|
67
|
+
# @example store a value
|
68
|
+
#
|
69
|
+
# stash.set('name') { 'Trigster' }
|
70
|
+
# => "Trigster"
|
71
|
+
# stash[:cash] = 'in the hash stash cache store'
|
72
|
+
# => "in the hash stash cache store"
|
73
|
+
# data = { id: 'trig', name: 'Trigster Jay', passwd: 'f00d' }
|
74
|
+
# stash[:juser] = data
|
75
|
+
# => {
|
76
|
+
# :id => "trig",
|
77
|
+
# :name => "Trigster Jay",
|
78
|
+
# :passwd => "f00d"
|
79
|
+
# }
|
80
|
+
#
|
81
|
+
# @param key [Symbol, String] string or symbol representing the key
|
82
|
+
# @param value [Object] any object that represents the value (optional)
|
83
|
+
# @param block [&block] that returns the value to set (optional)
|
84
|
+
#
|
85
|
+
# @return nothing.
|
86
|
+
#
|
87
|
+
def []=(key, value)
|
88
|
+
@store[key] = value
|
89
|
+
end
|
90
|
+
|
91
|
+
# Iterates over all key-value pairs.
|
92
|
+
#
|
93
|
+
# @param block [&block] that will receive the key/value of each pair
|
94
|
+
#
|
95
|
+
# @yield the string key and value.
|
96
|
+
#
|
97
|
+
def each(&block)
|
98
|
+
@store.each { |k, v| yield(k, v) }
|
99
|
+
end
|
100
|
+
|
101
|
+
# Loads a hash of data into the stash.
|
102
|
+
#
|
103
|
+
# @param hash [Hash] of data with either String or Symbol keys.
|
104
|
+
#
|
105
|
+
# @return nothing.
|
106
|
+
#
|
107
|
+
def load(data)
|
108
|
+
data.each do |key, value|
|
109
|
+
@store[key] = value
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# return the size of the store as an integer
|
114
|
+
#
|
115
|
+
# @return [Fixnum]
|
116
|
+
#
|
117
|
+
def size
|
118
|
+
@store.size
|
119
|
+
end
|
120
|
+
|
121
|
+
# return a boolean indicating presence of the given key in the store
|
122
|
+
#
|
123
|
+
# @param key [Symbol, String] a string or symbol representing the key
|
124
|
+
#
|
125
|
+
# @return [TrueClass, FalseClass]
|
126
|
+
#
|
127
|
+
def include?(key)
|
128
|
+
@store.include? key
|
129
|
+
end
|
130
|
+
alias_method :key?, :include?
|
131
|
+
|
132
|
+
# return a boolean indicating presence of the given value in the store
|
133
|
+
#
|
134
|
+
# @param value [String] a string representing the value
|
135
|
+
#
|
136
|
+
# @return [TrueClass, FalseClass]
|
137
|
+
#
|
138
|
+
def value?(value)
|
139
|
+
@store.value? value
|
140
|
+
end
|
141
|
+
|
142
|
+
# return all keys in the store as an array
|
143
|
+
#
|
144
|
+
# @return [Array<String, Symbol>] all the keys in store
|
145
|
+
#
|
146
|
+
def keys
|
147
|
+
@store.keys
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
data/lib/hoodie/utils.rb
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Author: Stefano Harding <riddopic@gmail.com>
|
4
|
+
#
|
5
|
+
# Copyright (C) 2014 Stefano Harding
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
module Hoodie
|
21
|
+
# Returns an aligned_string of text relative to the size of the terminal
|
22
|
+
# window. If a line in the string exceeds the width of the terminal window
|
23
|
+
# the line will be chopped off at the whitespace chacter closest to the
|
24
|
+
# end of the line and prepended to the next line, keeping all indentation.
|
25
|
+
#
|
26
|
+
# The terminal size is detected by default, but custom line widths can
|
27
|
+
# passed. All strings will also be left aligned with 5 whitespace characters
|
28
|
+
# by default.
|
29
|
+
def self.align_text(text, console_cols = nil, preamble = 5)
|
30
|
+
unless console_cols
|
31
|
+
console_cols = terminal_dimensions[0]
|
32
|
+
|
33
|
+
# if unknown size we default to the typical unix default
|
34
|
+
console_cols = 80 if console_cols == 0
|
35
|
+
end
|
36
|
+
|
37
|
+
console_cols -= preamble
|
38
|
+
|
39
|
+
# Return unaligned text if console window is too small
|
40
|
+
return text if console_cols <= 0
|
41
|
+
|
42
|
+
# If console is 0 this implies unknown so we assume the common
|
43
|
+
# minimal unix configuration of 80 characters
|
44
|
+
console_cols = 80 if console_cols <= 0
|
45
|
+
|
46
|
+
text = text.split("\n")
|
47
|
+
piece = ''
|
48
|
+
whitespace = 0
|
49
|
+
|
50
|
+
text.each_with_index do |line, i|
|
51
|
+
whitespace = 0
|
52
|
+
|
53
|
+
while whitespace < line.length && line[whitespace].chr == ' '
|
54
|
+
whitespace += 1
|
55
|
+
end
|
56
|
+
|
57
|
+
# If the current line is empty, indent it so that a snippet
|
58
|
+
# from the previous line is aligned correctly.
|
59
|
+
if line == ""
|
60
|
+
line = (" " * whitespace)
|
61
|
+
end
|
62
|
+
|
63
|
+
# If text was snipped from the previous line, prepend it to the
|
64
|
+
# current line after any current indentation.
|
65
|
+
if piece != ''
|
66
|
+
# Reset whitespaces to 0 if there are more whitespaces than there are
|
67
|
+
# console columns
|
68
|
+
whitespace = 0 if whitespace >= console_cols
|
69
|
+
|
70
|
+
# If the current line is empty and being prepended to, create a new
|
71
|
+
# empty line in the text so that formatting is preserved.
|
72
|
+
if text[i + 1] && line == (" " * whitespace)
|
73
|
+
text.insert(i + 1, "")
|
74
|
+
end
|
75
|
+
|
76
|
+
# Add the snipped text to the current line
|
77
|
+
line.insert(whitespace, "#{piece} ")
|
78
|
+
end
|
79
|
+
|
80
|
+
piece = ''
|
81
|
+
|
82
|
+
# Compare the line length to the allowed line length.
|
83
|
+
# If it exceeds it, snip the offending text from the line
|
84
|
+
# and store it so that it can be prepended to the next line.
|
85
|
+
if line.length > (console_cols + preamble)
|
86
|
+
reverse = console_cols
|
87
|
+
|
88
|
+
while line[reverse].chr != ' '
|
89
|
+
reverse -= 1
|
90
|
+
end
|
91
|
+
|
92
|
+
piece = line.slice!(reverse, (line.length - 1)).lstrip
|
93
|
+
end
|
94
|
+
|
95
|
+
# If a snippet exists when all the columns in the text have been
|
96
|
+
# updated, create a new line and append the snippet to it, using
|
97
|
+
# the same left alignment as the last line in the text.
|
98
|
+
if piece != '' && text[i+1].nil?
|
99
|
+
text[i+1] = "#{' ' * (whitespace)}#{piece}"
|
100
|
+
piece = ''
|
101
|
+
end
|
102
|
+
|
103
|
+
# Add the preamble to the line and add it to the text
|
104
|
+
line = ((' ' * preamble) + line)
|
105
|
+
text[i] = line
|
106
|
+
end
|
107
|
+
|
108
|
+
text.join("\n")
|
109
|
+
end
|
110
|
+
|
111
|
+
# Figures out the columns and lines of the current tty
|
112
|
+
#
|
113
|
+
# Returns [0, 0] if it can't figure it out or if you're
|
114
|
+
# not running on a tty
|
115
|
+
def self.terminal_dimensions(stdout = STDOUT, environment = ENV)
|
116
|
+
return [0, 0] unless stdout.tty?
|
117
|
+
|
118
|
+
return [80, 40] if Util.windows?
|
119
|
+
|
120
|
+
if environment["COLUMNS"] && environment["LINES"]
|
121
|
+
return [environment["COLUMNS"].to_i, environment["LINES"].to_i]
|
122
|
+
|
123
|
+
elsif environment["TERM"] && command_in_path?("tput")
|
124
|
+
return [`tput cols`.to_i, `tput lines`.to_i]
|
125
|
+
|
126
|
+
elsif command_in_path?('stty')
|
127
|
+
return `stty size`.scan(/\d+/).map {|s| s.to_i }
|
128
|
+
else
|
129
|
+
return [0, 0]
|
130
|
+
end
|
131
|
+
rescue
|
132
|
+
[0, 0]
|
133
|
+
end
|
134
|
+
end
|