garcun 0.0.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.
Files changed (139) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +17 -0
  3. data/.gitignore +197 -0
  4. data/.rspec +2 -0
  5. data/Gemfile +22 -0
  6. data/LICENSE +201 -0
  7. data/README.md +521 -0
  8. data/Rakefile +47 -0
  9. data/garcun.gemspec +83 -0
  10. data/lib/garcon.rb +290 -0
  11. data/lib/garcon/chef/chef_helpers.rb +343 -0
  12. data/lib/garcon/chef/coerce/coercer.rb +134 -0
  13. data/lib/garcon/chef/coerce/coercions/boolean_definitions.rb +34 -0
  14. data/lib/garcon/chef/coerce/coercions/date_definitions.rb +32 -0
  15. data/lib/garcon/chef/coerce/coercions/date_time_definitions.rb +32 -0
  16. data/lib/garcon/chef/coerce/coercions/fixnum_definitions.rb +34 -0
  17. data/lib/garcon/chef/coerce/coercions/float_definitions.rb +32 -0
  18. data/lib/garcon/chef/coerce/coercions/hash_definitions.rb +29 -0
  19. data/lib/garcon/chef/coerce/coercions/integer_definitions.rb +31 -0
  20. data/lib/garcon/chef/coerce/coercions/string_definitions.rb +45 -0
  21. data/lib/garcon/chef/coerce/coercions/time_definitions.rb +32 -0
  22. data/lib/garcon/chef/handler/devreporter.rb +127 -0
  23. data/lib/garcon/chef/log.rb +64 -0
  24. data/lib/garcon/chef/node.rb +100 -0
  25. data/lib/garcon/chef/provider/civilize.rb +209 -0
  26. data/lib/garcon/chef/provider/development.rb +159 -0
  27. data/lib/garcon/chef/provider/download.rb +420 -0
  28. data/lib/garcon/chef/provider/house_keeping.rb +265 -0
  29. data/lib/garcon/chef/provider/node_cache.rb +31 -0
  30. data/lib/garcon/chef/provider/partial.rb +183 -0
  31. data/lib/garcon/chef/provider/recovery.rb +80 -0
  32. data/lib/garcon/chef/provider/zip_file.rb +271 -0
  33. data/lib/garcon/chef/resource/attribute.rb +52 -0
  34. data/lib/garcon/chef/resource/base_dsl.rb +174 -0
  35. data/lib/garcon/chef/resource/blender.rb +140 -0
  36. data/lib/garcon/chef/resource/lazy_eval.rb +66 -0
  37. data/lib/garcon/chef/resource/resource_name.rb +109 -0
  38. data/lib/garcon/chef/secret_bag.rb +204 -0
  39. data/lib/garcon/chef/validations.rb +76 -0
  40. data/lib/garcon/chef_inclusions.rb +151 -0
  41. data/lib/garcon/configuration.rb +138 -0
  42. data/lib/garcon/core_ext.rb +39 -0
  43. data/lib/garcon/core_ext/array.rb +27 -0
  44. data/lib/garcon/core_ext/binding.rb +64 -0
  45. data/lib/garcon/core_ext/boolean.rb +66 -0
  46. data/lib/garcon/core_ext/duration.rb +271 -0
  47. data/lib/garcon/core_ext/enumerable.rb +34 -0
  48. data/lib/garcon/core_ext/file.rb +127 -0
  49. data/lib/garcon/core_ext/filetest.rb +62 -0
  50. data/lib/garcon/core_ext/hash.rb +279 -0
  51. data/lib/garcon/core_ext/kernel.rb +159 -0
  52. data/lib/garcon/core_ext/lazy.rb +222 -0
  53. data/lib/garcon/core_ext/method_access.rb +243 -0
  54. data/lib/garcon/core_ext/module.rb +92 -0
  55. data/lib/garcon/core_ext/nil.rb +53 -0
  56. data/lib/garcon/core_ext/numeric.rb +44 -0
  57. data/lib/garcon/core_ext/object.rb +342 -0
  58. data/lib/garcon/core_ext/pathname.rb +152 -0
  59. data/lib/garcon/core_ext/process.rb +41 -0
  60. data/lib/garcon/core_ext/random.rb +497 -0
  61. data/lib/garcon/core_ext/string.rb +312 -0
  62. data/lib/garcon/core_ext/struct.rb +49 -0
  63. data/lib/garcon/core_ext/symbol.rb +170 -0
  64. data/lib/garcon/core_ext/time.rb +234 -0
  65. data/lib/garcon/exceptions.rb +101 -0
  66. data/lib/garcon/inflections.rb +237 -0
  67. data/lib/garcon/inflections/defaults.rb +79 -0
  68. data/lib/garcon/inflections/inflections.rb +182 -0
  69. data/lib/garcon/inflections/rules_collection.rb +37 -0
  70. data/lib/garcon/secret.rb +271 -0
  71. data/lib/garcon/stash/format.rb +114 -0
  72. data/lib/garcon/stash/journal.rb +226 -0
  73. data/lib/garcon/stash/queue.rb +83 -0
  74. data/lib/garcon/stash/serializer.rb +86 -0
  75. data/lib/garcon/stash/store.rb +435 -0
  76. data/lib/garcon/task.rb +31 -0
  77. data/lib/garcon/task/atomic.rb +151 -0
  78. data/lib/garcon/task/atomic_boolean.rb +127 -0
  79. data/lib/garcon/task/condition.rb +99 -0
  80. data/lib/garcon/task/copy_on_notify_observer_set.rb +154 -0
  81. data/lib/garcon/task/copy_on_write_observer_set.rb +153 -0
  82. data/lib/garcon/task/count_down_latch.rb +92 -0
  83. data/lib/garcon/task/delay.rb +196 -0
  84. data/lib/garcon/task/dereferenceable.rb +144 -0
  85. data/lib/garcon/task/event.rb +119 -0
  86. data/lib/garcon/task/executor.rb +275 -0
  87. data/lib/garcon/task/executor_options.rb +59 -0
  88. data/lib/garcon/task/future.rb +107 -0
  89. data/lib/garcon/task/immediate_executor.rb +84 -0
  90. data/lib/garcon/task/ivar.rb +171 -0
  91. data/lib/garcon/task/lazy_reference.rb +74 -0
  92. data/lib/garcon/task/monotonic_time.rb +69 -0
  93. data/lib/garcon/task/obligation.rb +256 -0
  94. data/lib/garcon/task/observable.rb +101 -0
  95. data/lib/garcon/task/priority_queue.rb +234 -0
  96. data/lib/garcon/task/processor_count.rb +128 -0
  97. data/lib/garcon/task/read_write_lock.rb +304 -0
  98. data/lib/garcon/task/safe_task_executor.rb +58 -0
  99. data/lib/garcon/task/single_thread_executor.rb +97 -0
  100. data/lib/garcon/task/thread_pool/cached.rb +71 -0
  101. data/lib/garcon/task/thread_pool/executor.rb +294 -0
  102. data/lib/garcon/task/thread_pool/fixed.rb +61 -0
  103. data/lib/garcon/task/thread_pool/worker.rb +90 -0
  104. data/lib/garcon/task/timer.rb +44 -0
  105. data/lib/garcon/task/timer_set.rb +194 -0
  106. data/lib/garcon/task/timer_task.rb +377 -0
  107. data/lib/garcon/task/waitable_list.rb +58 -0
  108. data/lib/garcon/utility/ansi.rb +199 -0
  109. data/lib/garcon/utility/at_random.rb +77 -0
  110. data/lib/garcon/utility/crypto.rb +292 -0
  111. data/lib/garcon/utility/equalizer.rb +146 -0
  112. data/lib/garcon/utility/faker/extensions/array.rb +22 -0
  113. data/lib/garcon/utility/faker/extensions/symbol.rb +9 -0
  114. data/lib/garcon/utility/faker/faker.rb +164 -0
  115. data/lib/garcon/utility/faker/faker/company.rb +17 -0
  116. data/lib/garcon/utility/faker/faker/hacker.rb +30 -0
  117. data/lib/garcon/utility/faker/faker/version.rb +3 -0
  118. data/lib/garcon/utility/faker/locales/en-US.yml +83 -0
  119. data/lib/garcon/utility/faker/locales/en.yml +21 -0
  120. data/lib/garcon/utility/file_helper.rb +170 -0
  121. data/lib/garcon/utility/hookers.rb +178 -0
  122. data/lib/garcon/utility/interpolation.rb +90 -0
  123. data/lib/garcon/utility/memstash.rb +364 -0
  124. data/lib/garcon/utility/misc.rb +54 -0
  125. data/lib/garcon/utility/msg_from_god.rb +62 -0
  126. data/lib/garcon/utility/retry.rb +238 -0
  127. data/lib/garcon/utility/timeout.rb +58 -0
  128. data/lib/garcon/utility/uber/builder.rb +91 -0
  129. data/lib/garcon/utility/uber/callable.rb +7 -0
  130. data/lib/garcon/utility/uber/delegates.rb +13 -0
  131. data/lib/garcon/utility/uber/inheritable_attr.rb +37 -0
  132. data/lib/garcon/utility/uber/options.rb +101 -0
  133. data/lib/garcon/utility/uber/uber_version.rb +3 -0
  134. data/lib/garcon/utility/uber/version.rb +33 -0
  135. data/lib/garcon/utility/url_helper.rb +100 -0
  136. data/lib/garcon/utils.rb +29 -0
  137. data/lib/garcon/version.rb +62 -0
  138. data/lib/garcun.rb +24 -0
  139. metadata +680 -0
@@ -0,0 +1,66 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ # License: Apache License, Version 2.0
5
+ # Copyright: (C) 2014-2015 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
+ # Add #blank? method to TrueClass class.
21
+ class TrueClass
22
+ # True is never blank.
23
+ #
24
+ # @example
25
+ # true.blank? # => false
26
+ #
27
+ # @return [FalseClass]
28
+ #
29
+ # @api public
30
+ def blank?
31
+ false
32
+ end
33
+
34
+ # Since TrueClass is immutable it cannot be duplicated.
35
+ # For this reason #try_dup returns +self+.
36
+ #
37
+ # true.dup! #=> true
38
+ #
39
+ def dup! ; self ; end
40
+ def dup? ; false ; end
41
+ def clone? ; false ; end
42
+ end
43
+
44
+ # Add #blank? method to FalseClass class.
45
+ class FalseClass
46
+ # False is always blank.
47
+ #
48
+ # @example
49
+ # false.blank? # => true
50
+ #
51
+ # @return [TrueClass]
52
+ #
53
+ # @api public
54
+ def blank?
55
+ true
56
+ end
57
+
58
+ # Since FalseClass is immutable it cannot be duplicated.
59
+ # For this reason #try_dup returns +self+.
60
+ #
61
+ # false.dup! #=> false
62
+ #
63
+ def dup! ; self ; end
64
+ def dup? ; false ; end
65
+ def clone? ; false ; end
66
+ end
@@ -0,0 +1,271 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ # License: Apache License, Version 2.0
5
+ # Copyright: (C) 2014-2015 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
+ class Duration
21
+ include Comparable
22
+
23
+ SECOND = 1
24
+ MINUTE = 60 * SECOND
25
+ HOUR = 60 * MINUTE
26
+ DAY = 24 * HOUR
27
+ WEEK = 7 * DAY
28
+ YEAR = 365 * DAY
29
+
30
+ SEGMENTS = %w{years weeks days hours minutes seconds}.collect{ |s| s.to_sym }
31
+
32
+ def self.[](seconds, *segments)
33
+ new(seconds, *segments)
34
+ end
35
+
36
+ def initialize(seconds=0, *segments)
37
+ @seconds = seconds.to_i
38
+ reset_segments(*segments)
39
+ end
40
+
41
+ def segments; @segments; end
42
+
43
+ def reset_segments(*segments)
44
+ case segments.size
45
+ when 0
46
+ @segments = [:days, :hours, :minutes, :seconds]
47
+ when 1
48
+ case segments = segments[0]
49
+ when Array
50
+ @segments = segments.collect { |p| (p.to_s.downcase.chomp('s') + 's').to_sym }
51
+ raise ArgumentError unless @segments.all? { |s| SEGMENTS.include?(s) }
52
+ else
53
+ f = SEGMENTS.index(segments)
54
+ @segments = SEGMENTS[f..0]
55
+ end
56
+ when 2
57
+ f = SEGMENTS.index(segments[0])
58
+ t = SEGMENTS.index(segments[1])
59
+ @segments = SEGMENTS[f..t]
60
+ else
61
+ raise ArgumentError
62
+ end
63
+ end
64
+
65
+ def inspect
66
+ h = to_h
67
+ segments.reverse.collect do |l|
68
+ "#{h[l.to_sym]} #{l}"
69
+ end.join(' ')
70
+ end
71
+
72
+ def to_i ; @seconds.to_i ; end
73
+ def to_f ; @seconds.to_f ; end
74
+
75
+ public
76
+
77
+ def to_a
78
+ a, s = [], @seconds
79
+ a[5], s = *s.divmod(YEAR) if @segments.include?(:years)
80
+ a[4], s = *s.divmod(WEEK) if @segments.include?(:weeks)
81
+ a[3], s = *s.divmod(DAY) if @segments.include?(:days)
82
+ a[2], s = *s.divmod(HOUR) if @segments.include?(:hours)
83
+ a[1], s = *s.divmod(MINUTE) if @segments.include?(:minutes)
84
+ a[0], s = *s.divmod(SECOND) if @segments.include?(:seconds)
85
+ a.compact.reverse
86
+ end
87
+
88
+ def to_h
89
+ h, s = {}, @seconds
90
+ h[:years], s = *s.divmod(YEAR) if @segments.include?(:years)
91
+ h[:weeks], s = *s.divmod(WEEK) if @segments.include?(:weeks)
92
+ h[:days], s = *s.divmod(DAY) if @segments.include?(:days)
93
+ h[:hours], s = *s.divmod(HOUR) if @segments.include?(:hours)
94
+ h[:minutes], s = *s.divmod(MINUTE) if @segments.include?(:minutes)
95
+ h[:seconds], s = *s.divmod(SECOND) if @segments.include?(:seconds)
96
+ h
97
+ end
98
+
99
+ def to_s
100
+ h = to_h
101
+ segments.reverse.collect do |l|
102
+ "#{h[l.to_sym]} #{l}"
103
+ end.join(' ')
104
+ end
105
+
106
+ def ==(other)
107
+ if Duration === other
108
+ other.seconds == seconds
109
+ else
110
+ other == seconds
111
+ end
112
+ end
113
+
114
+ def <=>(other)
115
+ @seconds <=> other.to_i
116
+ end
117
+
118
+ def years ; to_h[:years] ; end
119
+ def weeks ; to_h[:weeks] ; end
120
+ def days ; to_h[:days] ; end
121
+ def hours ; to_h[:hours] ; end
122
+ def minutes ; to_h[:minutes] ; end
123
+ def seconds ; to_h[:seconds] ; end
124
+
125
+ def total ; seconds ; end
126
+
127
+ def +(other)
128
+ self.class.new(@seconds + other.to_i, segments)
129
+ end
130
+
131
+ def -(other)
132
+ self.class.new(@seconds - other.to_i, segments)
133
+ end
134
+
135
+ def *(other)
136
+ self.class.new(@seconds * other.to_i, segments)
137
+ end
138
+
139
+ def /(other)
140
+ self.class.new(@seconds / other.to_i, segments)
141
+ end
142
+
143
+ def segmented(*segments)
144
+ self.class.new(@seconds, segments)
145
+ end
146
+
147
+ # Format duration.
148
+ #
149
+ # *Identifiers*
150
+ #
151
+ # %w -- Number of weeks
152
+ # %d -- Number of days
153
+ # %h -- Number of hours
154
+ # %m -- Number of minutes
155
+ # %s -- Number of seconds
156
+ # %t -- Total number of seconds
157
+ # %x -- Duration#to_s
158
+ # %% -- Literal `%' character
159
+ #
160
+ # *Example*
161
+ #
162
+ # d = Duration.new(:weeks => 10, :days => 7)
163
+ # => #<Duration: 11 weeks>
164
+ # d.strftime("It's been %w weeks!")
165
+ # => "It's been 11 weeks!"
166
+ #
167
+ def strftime(fmt)
168
+ h = to_h
169
+ hx = {
170
+ 'y' => h[:years] ,
171
+ 'w' => h[:weeks] ,
172
+ 'd' => h[:days] ,
173
+ 'h' => h[:hours] ,
174
+ 'm' => h[:minutes],
175
+ 's' => h[:seconds],
176
+ 't' => total,
177
+ 'x' => to_s
178
+ }
179
+ fmt.gsub(/%?%(w|d|h|m|s|t|x)/) do |match|
180
+ hx[match[1..1]]
181
+ end.gsub('%%', '%')
182
+ end
183
+
184
+ def -@
185
+ self.class.new(-@seconds)
186
+ end
187
+
188
+ def +@
189
+ self.class.new(+@seconds)
190
+ end
191
+
192
+ def before(time)
193
+ @seconds.before(time)
194
+ end
195
+
196
+ def after(time)
197
+ @seconds.after(time)
198
+ end
199
+
200
+
201
+ # = Numeric Extensions for Durations
202
+ #
203
+ module Numeric
204
+
205
+ # Enables the use of time calculations and declarations,
206
+ # like 45.minutes + 2.hours + 4.years. The base unit for
207
+ # all of these Numeric time methods is seconds.
208
+ def seconds ; Duration[self] ; end
209
+ alias_method :second, :seconds
210
+
211
+ # Converts minutes into seconds.
212
+ def minutes ; Duration[self * 60] ; end
213
+ alias_method :minute, :minutes
214
+
215
+ # Converts hours into seconds.
216
+ def hours ; Duration[self * 3600] ; end
217
+ alias_method :hour, :hours
218
+ #def as_hours ; self / 60.minutes ; end
219
+
220
+ # Converts days into seconds.
221
+ def days ; Duration[self * 86400] ; end
222
+ alias_method :day, :days
223
+
224
+ # Converts weeks into seconds.
225
+ def weeks ; Duration[self * 604800] ; end
226
+ alias_method :week, :weeks
227
+
228
+ # Converts fortnights into seconds.
229
+ # (A fortnight is 2 weeks)
230
+ def fortnights ; Duration[self * 1209600] ; end
231
+ alias_method :fortnight, :fortnights
232
+
233
+ # Converts months into seconds.
234
+ # WARNING: This is not exact as it assumes 30 days to a month.
235
+ def months ; Duration[self * 30 * 86400] ; end
236
+ alias_method :month, :months
237
+
238
+ # Converts years into seconds.
239
+ # WARNING: This is not exact as it assumes 365 days to a year.
240
+ # ie. It doesn not account for leap years.
241
+ def years ; Duration[self * 365 * 86400, :years] ; end
242
+ alias_method :year, :years
243
+
244
+ end
245
+
246
+ # Time#duration has been added to convert the UNIX timestamp into a Duration.
247
+ # See Time#duration for an example.
248
+ #
249
+ module Time
250
+ # Create a Duration object from the UNIX timestamp.
251
+ #
252
+ # *Example*
253
+ #
254
+ # Time.now.duration
255
+ # => #<Duration: 1898 weeks, 6 days, 1 hour, 12 minutes and 1 second>
256
+ #
257
+ def duration
258
+ Duration[to_i]
259
+ end
260
+ end
261
+
262
+ end
263
+
264
+ class Numeric
265
+ include Duration::Numeric
266
+ end
267
+
268
+ class Time
269
+ include Duration::Time
270
+ end
271
+
@@ -0,0 +1,34 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ # License: Apache License, Version 2.0
5
+ # Copyright: (C) 2014-2015 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
+ # Add #exclude
21
+ module Enumerable
22
+ unless method_defined?(:exclude?)
23
+ # The inverse of #include?.
24
+ #
25
+ # @example
26
+ # [:a, :b].exclude?(:c) # => true
27
+ # [:a, :b].exclude?(:a) # => false
28
+ #
29
+ # @api public
30
+ def exclude?(object)
31
+ !include?(object)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,127 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ # License: Apache License, Version 2.0
5
+ # Copyright: (C) 2014-2015 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 'thread' unless defined?(Thread)
21
+ require 'tempfile' unless defined?(Tempfile)
22
+ require 'fileutils' unless defined?(FileUtils)
23
+
24
+ class File
25
+
26
+ def self.atomic_id
27
+ @atomic_id ||= 0
28
+ @atomic_id += 1
29
+ end
30
+
31
+ # Same as `File.open`, but acts on a temporary copy of named
32
+ # file, copying the file back to the original on completion.
33
+ #
34
+ # @uncommon
35
+ # require 'facets/fileutils/atomic_open'
36
+ #
37
+ def self.atomic_open(file_name, mode="r", temp_dir=nil, &block)
38
+ temp_dir = temp_dir || Dir.tmpdir
39
+ temp_file = Tempfile.new("#{aomtic_id}-" + basename(file_name), temp_dir)
40
+
41
+ if File.exist?(file_name)
42
+ FileUtils.cp(file_name, temp_file)
43
+ end
44
+
45
+ open(temp_file, mode, &block)
46
+
47
+ FileUtils.cp(temp_file, file_name)
48
+ end
49
+
50
+ # Write to a file atomically. Useful for situations where you don't
51
+ # want other processes or threads to see half-written files.
52
+ #
53
+ # File.atomic_write("important.txt") do |file|
54
+ # file.write("hello")
55
+ # end
56
+ #
57
+ # If your temporary directory is not on the same filesystem as the file you're
58
+ # trying to write, you can provide a different temporary directory.
59
+ #
60
+ # File.atomic_write("important.txt", "tmp") do |file|
61
+ # file.write("hello")
62
+ # end
63
+ #
64
+ # NOTE: This method is not a common core extension and is not
65
+ # loaded automatically when using <code>require 'facets'</code>.
66
+ #
67
+ # CREDIT: David Heinemeier Hansson
68
+ #
69
+ # @uncommon
70
+ # require 'facets/fileutils/atomic_write'
71
+ #
72
+ def self.atomic_write(file_name, temp_dir=nil)
73
+ temp_dir = temp_dir || Dir.tmpdir
74
+ temp_file = Tempfile.new(basename(file_name), temp_dir)
75
+
76
+ yield temp_file
77
+ temp_file.close
78
+
79
+ begin
80
+ ## Get original file permissions
81
+ old_stat = stat(file_name)
82
+ rescue Errno::ENOENT
83
+ ## No old permissions, write a temp file to determine the defaults
84
+ check_name = join(dirname(file_name), ".permissions_check.#{Thread.current.object_id}.#{Process.pid}.#{rand(1000000)}")
85
+ open(check_name, "w") { }
86
+ old_stat = stat(check_name)
87
+ unlink(check_name)
88
+ end
89
+
90
+ ## Overwrite original file with temp file
91
+ FileUtils.mv(temp_file.path, file_name)
92
+
93
+ ## Set correct permissions on new file
94
+ chown(old_stat.uid, old_stat.gid, file_name)
95
+ chmod(old_stat.mode, file_name)
96
+ end
97
+
98
+ # Reads in a file, removes blank lines and removes lines starting
99
+ # with '#' and then returns an array of all the remaining lines.
100
+ #
101
+ # Thr remark indicator can be overridden via the +:omit:+ option, which
102
+ # can be a regualar expression or a string that is match against the
103
+ # start of a line.
104
+ #
105
+ # CREDIT: Trans
106
+
107
+ def self.read_list(filepath, options={})
108
+ chomp = options[:chomp]
109
+ omit = case options[:omit]
110
+ when Regexp
111
+ omit
112
+ when nil
113
+ /^\s*\#/
114
+ else
115
+ /^\s*#{Regexp.escape(omit)}/
116
+ end
117
+
118
+ list = []
119
+ readlines(filepath).each do |line|
120
+ line = line.strip.chomp(chomp)
121
+ next if line.empty?
122
+ next if omit === line
123
+ list << line
124
+ end
125
+ list
126
+ end
127
+ end