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.
@@ -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
@@ -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