hoodie 0.1.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.
- 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
|