solutious-rudy 0.7.3 → 0.7.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. data/CHANGES.txt +28 -5
  2. data/README.rdoc +1 -1
  3. data/Rudyfile +51 -19
  4. data/bin/ird +153 -0
  5. data/bin/rudy +18 -23
  6. data/bin/rudy-ec2 +5 -10
  7. data/bin/rudy-s3 +1 -3
  8. data/bin/rudy-sdb +0 -2
  9. data/examples/README.md +10 -0
  10. data/examples/debian-sinatra-passenger/commands.rb +19 -0
  11. data/examples/debian-sinatra-passenger/machines.rb +32 -0
  12. data/examples/debian-sinatra-passenger/routines.rb +30 -0
  13. data/examples/debian-sinatra-thin/commands.rb +17 -0
  14. data/examples/debian-sinatra-thin/machines.rb +35 -0
  15. data/examples/debian-sinatra-thin/routines.rb +72 -0
  16. data/lib/rudy.rb +8 -17
  17. data/lib/rudy/aws.rb +1 -2
  18. data/lib/rudy/aws/ec2/address.rb +0 -1
  19. data/lib/rudy/aws/ec2/snapshot.rb +11 -0
  20. data/lib/rudy/aws/s3.rb +6 -3
  21. data/lib/rudy/cli.rb +12 -2
  22. data/lib/rudy/cli/aws/ec2/candy.rb +19 -48
  23. data/lib/rudy/cli/aws/ec2/images.rb +109 -122
  24. data/lib/rudy/cli/aws/s3/buckets.rb +1 -2
  25. data/lib/rudy/cli/config.rb +13 -0
  26. data/lib/rudy/cli/disks.rb +24 -1
  27. data/lib/rudy/cli/routines.rb +10 -11
  28. data/lib/rudy/config.rb +6 -9
  29. data/lib/rudy/config/objects.rb +4 -0
  30. data/lib/rudy/global.rb +4 -5
  31. data/lib/rudy/huxtable.rb +23 -8
  32. data/lib/rudy/machines.rb +11 -4
  33. data/lib/rudy/metadata.rb +8 -94
  34. data/lib/rudy/metadata/backup.rb +113 -0
  35. data/lib/rudy/metadata/backups.rb +65 -0
  36. data/lib/rudy/{disks.rb → metadata/disk.rb} +36 -69
  37. data/lib/rudy/metadata/disks.rb +67 -0
  38. data/lib/rudy/metadata/objectbase.rb +104 -0
  39. data/lib/rudy/mixins.rb +2 -0
  40. data/lib/rudy/routines.rb +173 -88
  41. data/lib/rudy/routines/helpers/dependshelper.rb +34 -0
  42. data/lib/rudy/routines/helpers/diskhelper.rb +174 -5
  43. data/lib/rudy/routines/helpers/scmhelper.rb +2 -2
  44. data/lib/rudy/routines/helpers/scripthelper.rb +11 -4
  45. data/lib/rudy/routines/passthrough.rb +3 -1
  46. data/lib/rudy/routines/reboot.rb +75 -0
  47. data/lib/rudy/routines/startup.rb +3 -3
  48. data/lib/rudy/scm/git.rb +17 -17
  49. data/lib/rudy/scm/svn.rb +46 -5
  50. data/lib/rudy/utils.rb +3 -2
  51. data/rudy.gemspec +24 -46
  52. data/test/30_sdb_metadata/10_disks_test.rb +5 -5
  53. metadata +35 -67
  54. data/lib/annoy.rb +0 -298
  55. data/lib/console.rb +0 -404
  56. data/lib/escape.rb +0 -305
  57. data/lib/rudy/backup.rb +0 -135
  58. data/lib/storable.rb +0 -292
  59. data/lib/sysinfo.rb +0 -285
  60. data/lib/tryouts.rb +0 -33
  61. data/vendor/highline-1.5.1/CHANGELOG +0 -222
  62. data/vendor/highline-1.5.1/INSTALL +0 -35
  63. data/vendor/highline-1.5.1/LICENSE +0 -7
  64. data/vendor/highline-1.5.1/README +0 -63
  65. data/vendor/highline-1.5.1/Rakefile +0 -82
  66. data/vendor/highline-1.5.1/TODO +0 -6
  67. data/vendor/highline-1.5.1/examples/ansi_colors.rb +0 -38
  68. data/vendor/highline-1.5.1/examples/asking_for_arrays.rb +0 -18
  69. data/vendor/highline-1.5.1/examples/basic_usage.rb +0 -75
  70. data/vendor/highline-1.5.1/examples/color_scheme.rb +0 -32
  71. data/vendor/highline-1.5.1/examples/limit.rb +0 -12
  72. data/vendor/highline-1.5.1/examples/menus.rb +0 -65
  73. data/vendor/highline-1.5.1/examples/overwrite.rb +0 -19
  74. data/vendor/highline-1.5.1/examples/page_and_wrap.rb +0 -322
  75. data/vendor/highline-1.5.1/examples/password.rb +0 -7
  76. data/vendor/highline-1.5.1/examples/trapping_eof.rb +0 -22
  77. data/vendor/highline-1.5.1/examples/using_readline.rb +0 -17
  78. data/vendor/highline-1.5.1/lib/highline.rb +0 -758
  79. data/vendor/highline-1.5.1/lib/highline/color_scheme.rb +0 -120
  80. data/vendor/highline-1.5.1/lib/highline/compatibility.rb +0 -17
  81. data/vendor/highline-1.5.1/lib/highline/import.rb +0 -43
  82. data/vendor/highline-1.5.1/lib/highline/menu.rb +0 -395
  83. data/vendor/highline-1.5.1/lib/highline/question.rb +0 -463
  84. data/vendor/highline-1.5.1/lib/highline/system_extensions.rb +0 -193
  85. data/vendor/highline-1.5.1/setup.rb +0 -1360
  86. data/vendor/highline-1.5.1/test/tc_color_scheme.rb +0 -56
  87. data/vendor/highline-1.5.1/test/tc_highline.rb +0 -823
  88. data/vendor/highline-1.5.1/test/tc_import.rb +0 -54
  89. data/vendor/highline-1.5.1/test/tc_menu.rb +0 -429
  90. data/vendor/highline-1.5.1/test/ts_all.rb +0 -15
@@ -1,305 +0,0 @@
1
- # escape.rb - escape/unescape library for several formats
2
- #
3
- # Copyright (C) 2006,2007 Tanaka Akira <akr@fsij.org>
4
- #
5
- # Redistribution and use in source and binary forms, with or without
6
- # modification, are permitted provided that the following conditions are met:
7
- #
8
- # 1. Redistributions of source code must retain the above copyright notice, this
9
- # list of conditions and the following disclaimer.
10
- # 2. Redistributions in binary form must reproduce the above copyright notice,
11
- # this list of conditions and the following disclaimer in the documentation
12
- # and/or other materials provided with the distribution.
13
- # 3. The name of the author may not be used to endorse or promote products
14
- # derived from this software without specific prior written permission.
15
- #
16
- # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17
- # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18
- # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19
- # EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20
- # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
21
- # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22
- # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23
- # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
24
- # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
25
- # OF SUCH DAMAGE.
26
-
27
- # Escape module provides several escape functions.
28
- # * URI
29
- # * HTML
30
- # * shell command
31
- module Escape #:nodoc:all
32
- module_function
33
-
34
- class StringWrapper
35
- class << self
36
- alias new_no_dup new
37
- def new(str)
38
- new_no_dup(str.dup)
39
- end
40
- end
41
-
42
- def initialize(str)
43
- @str = str
44
- end
45
-
46
- def to_s
47
- @str.dup
48
- end
49
-
50
- def inspect
51
- "\#<#{self.class}: #{@str}>"
52
- end
53
-
54
- def ==(other)
55
- other.class == self.class && @str == other.instance_variable_get(:@str)
56
- end
57
- alias eql? ==
58
-
59
- def hash
60
- @str.hash
61
- end
62
- end
63
-
64
- class ShellEscaped < StringWrapper
65
- end
66
-
67
- # Escape.shell_command composes
68
- # a sequence of words to
69
- # a single shell command line.
70
- # All shell meta characters are quoted and
71
- # the words are concatenated with interleaving space.
72
- # It returns an instance of ShellEscaped.
73
- #
74
- # Escape.shell_command(["ls", "/"]) #=> #<Escape::ShellEscaped: ls />
75
- # Escape.shell_command(["echo", "*"]) #=> #<Escape::ShellEscaped: echo '*'>
76
- #
77
- # Note that system(*command) and
78
- # system(Escape.shell_command(command)) is roughly same.
79
- # There are two exception as follows.
80
- # * The first is that the later may invokes /bin/sh.
81
- # * The second is an interpretation of an array with only one element:
82
- # the element is parsed by the shell with the former but
83
- # it is recognized as single word with the later.
84
- # For example, system(*["echo foo"]) invokes echo command with an argument "foo".
85
- # But system(Escape.shell_command(["echo foo"])) invokes "echo foo" command without arguments (and it probably fails).
86
- def shell_command(*command)
87
- command = [command].flatten.compact # Delano
88
- s = command.map {|word| shell_single_word(word) }.join(' ')
89
- ShellEscaped.new_no_dup(s)
90
- end
91
-
92
- # Escape.shell_single_word quotes shell meta characters.
93
- # It returns an instance of ShellEscaped.
94
- #
95
- # The result string is always single shell word, even if
96
- # the argument is "".
97
- # Escape.shell_single_word("") returns #<Escape::ShellEscaped: ''>.
98
- #
99
- # Escape.shell_single_word("") #=> #<Escape::ShellEscaped: ''>
100
- # Escape.shell_single_word("foo") #=> #<Escape::ShellEscaped: foo>
101
- # Escape.shell_single_word("*") #=> #<Escape::ShellEscaped: '*'>
102
- def shell_single_word(str)
103
- return unless str
104
- str &&= str.to_s # Delano fix
105
- if str.empty?
106
- ShellEscaped.new_no_dup("''")
107
- elsif %r{\A[0-9A-Za-z+,./:=@_-]+\z} =~ str
108
- ShellEscaped.new(str)
109
- else
110
- result = ''
111
- str.scan(/('+)|[^']+/) {
112
- if $1
113
- result << %q{\'} * $1.length
114
- else
115
- result << "'#{$&}'"
116
- end
117
- }
118
- ShellEscaped.new_no_dup(result)
119
- end
120
- end
121
-
122
- class PercentEncoded < StringWrapper
123
- end
124
-
125
- # Escape.uri_segment escapes URI segment using percent-encoding.
126
- # It returns an instance of PercentEncoded.
127
- #
128
- # Escape.uri_segment("a/b") #=> #<Escape::PercentEncoded: a%2Fb>
129
- #
130
- # The segment is "/"-splitted element after authority before query in URI, as follows.
131
- #
132
- # scheme://authority/segment1/segment2/.../segmentN?query#fragment
133
- #
134
- # See RFC 3986 for details of URI.
135
- def uri_segment(str)
136
- # pchar - pct-encoded = unreserved / sub-delims / ":" / "@"
137
- # unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
138
- # sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
139
- s = str.gsub(%r{[^A-Za-z0-9\-._~!$&'()*+,;=:@]}n) {
140
- '%' + $&.unpack("H2")[0].upcase
141
- }
142
- PercentEncoded.new_no_dup(s)
143
- end
144
-
145
- # Escape.uri_path escapes URI path using percent-encoding.
146
- # The given path should be a sequence of (non-escaped) segments separated by "/".
147
- # The segments cannot contains "/".
148
- # It returns an instance of PercentEncoded.
149
- #
150
- # Escape.uri_path("a/b/c") #=> #<Escape::PercentEncoded: a/b/c>
151
- # Escape.uri_path("a?b/c?d/e?f") #=> #<Escape::PercentEncoded: a%3Fb/c%3Fd/e%3Ff>
152
- #
153
- # The path is the part after authority before query in URI, as follows.
154
- #
155
- # scheme://authority/path#fragment
156
- #
157
- # See RFC 3986 for details of URI.
158
- #
159
- # Note that this function is not appropriate to convert OS path to URI.
160
- def uri_path(str)
161
- s = str.gsub(%r{[^/]+}n) { uri_segment($&) }
162
- PercentEncoded.new_no_dup(s)
163
- end
164
-
165
-
166
- def html_form_fast(pairs, sep='&')
167
- s = pairs.map {|k, v|
168
- # query-chars - pct-encoded - x-www-form-urlencoded-delimiters =
169
- # unreserved / "!" / "$" / "'" / "(" / ")" / "*" / "," / ":" / "@" / "/" / "?"
170
- # query-char - pct-encoded = unreserved / sub-delims / ":" / "@" / "/" / "?"
171
- # query-char = pchar / "/" / "?" = unreserved / pct-encoded / sub-delims / ":" / "@" / "/" / "?"
172
- # unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
173
- # sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
174
- # x-www-form-urlencoded-delimiters = "&" / "+" / ";" / "="
175
- k = k.gsub(%r{[^0-9A-Za-z\-\._~:/?@!\$'()*,]}n) {
176
- '%' + $&.unpack("H2")[0].upcase
177
- }
178
- v = v.gsub(%r{[^0-9A-Za-z\-\._~:/?@!\$'()*,]}n) {
179
- '%' + $&.unpack("H2")[0].upcase
180
- }
181
- "#{k}=#{v}"
182
- }.join(sep)
183
- PercentEncoded.new_no_dup(s)
184
- end
185
-
186
-
187
- # Escape.html_form composes HTML form key-value pairs as a x-www-form-urlencoded encoded string.
188
- # It returns an instance of PercentEncoded.
189
- #
190
- # Escape.html_form takes an array of pair of strings or
191
- # an hash from string to string.
192
- #
193
- # Escape.html_form([["a","b"], ["c","d"]]) #=> #<Escape::PercentEncoded: a=b&c=d>
194
- # Escape.html_form({"a"=>"b", "c"=>"d"}) #=> #<Escape::PercentEncoded: a=b&c=d>
195
- #
196
- # In the array form, it is possible to use same key more than once.
197
- # (It is required for a HTML form which contains
198
- # checkboxes and select element with multiple attribute.)
199
- #
200
- # Escape.html_form([["k","1"], ["k","2"]]) #=> #<Escape::PercentEncoded: k=1&k=2>
201
- #
202
- # If the strings contains characters which must be escaped in x-www-form-urlencoded,
203
- # they are escaped using %-encoding.
204
- #
205
- # Escape.html_form([["k=","&;="]]) #=> #<Escape::PercentEncoded: k%3D=%26%3B%3D>
206
- #
207
- # The separator can be specified by the optional second argument.
208
- #
209
- # Escape.html_form([["a","b"], ["c","d"]], ";") #=> #<Escape::PercentEncoded: a=b;c=d>
210
- #
211
- # See HTML 4.01 for details.
212
- def html_form(pairs, sep='&')
213
- r = ''
214
- first = true
215
- pairs.each {|k, v|
216
- # query-chars - pct-encoded - x-www-form-urlencoded-delimiters =
217
- # unreserved / "!" / "$" / "'" / "(" / ")" / "*" / "," / ":" / "@" / "/" / "?"
218
- # query-char - pct-encoded = unreserved / sub-delims / ":" / "@" / "/" / "?"
219
- # query-char = pchar / "/" / "?" = unreserved / pct-encoded / sub-delims / ":" / "@" / "/" / "?"
220
- # unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
221
- # sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
222
- # x-www-form-urlencoded-delimiters = "&" / "+" / ";" / "="
223
- r << sep if !first
224
- first = false
225
- k.each_byte {|byte|
226
- ch = byte.chr
227
- if %r{[^0-9A-Za-z\-\._~:/?@!\$'()*,]}n =~ ch
228
- r << "%" << ch.unpack("H2")[0].upcase
229
- else
230
- r << ch
231
- end
232
- }
233
- r << '='
234
- v.each_byte {|byte|
235
- ch = byte.chr
236
- if %r{[^0-9A-Za-z\-\._~:/?@!\$'()*,]}n =~ ch
237
- r << "%" << ch.unpack("H2")[0].upcase
238
- else
239
- r << ch
240
- end
241
- }
242
- }
243
- PercentEncoded.new_no_dup(r)
244
- end
245
-
246
- class HTMLEscaped < StringWrapper
247
- end
248
-
249
-
250
- HTML_TEXT_ESCAPE_HASH = {
251
- '&' => '&amp;',
252
- '<' => '&lt;',
253
- '>' => '&gt;',
254
- }
255
-
256
-
257
- # Escape.html_text escapes a string appropriate for HTML text using character references.
258
- # It returns an instance of HTMLEscaped.
259
- #
260
- # It escapes 3 characters:
261
- # * '&' to '&amp;'
262
- # * '<' to '&lt;'
263
- # * '>' to '&gt;'
264
- #
265
- # Escape.html_text("abc") #=> #<Escape::HTMLEscaped: abc>
266
- # Escape.html_text("a & b < c > d") #=> #<Escape::HTMLEscaped: a &amp; b &lt; c &gt; d>
267
- #
268
- # This function is not appropriate for escaping HTML element attribute
269
- # because quotes are not escaped.
270
- def html_text(str)
271
- s = str.gsub(/[&<>]/) {|ch| HTML_TEXT_ESCAPE_HASH[ch] }
272
- HTMLEscaped.new_no_dup(s)
273
- end
274
-
275
-
276
- HTML_ATTR_ESCAPE_HASH = {
277
- '&' => '&amp;',
278
- '<' => '&lt;',
279
- '>' => '&gt;',
280
- '"' => '&quot;',
281
- }
282
-
283
-
284
- class HTMLAttrValue < StringWrapper
285
- end
286
-
287
- # Escape.html_attr_value encodes a string as a double-quoted HTML attribute using character references.
288
- # It returns an instance of HTMLAttrValue.
289
- #
290
- # Escape.html_attr_value("abc") #=> #<Escape::HTMLAttrValue: "abc">
291
- # Escape.html_attr_value("a&b") #=> #<Escape::HTMLAttrValue: "a&amp;b">
292
- # Escape.html_attr_value("ab&<>\"c") #=> #<Escape::HTMLAttrValue: "ab&amp;&lt;&gt;&quot;c">
293
- # Escape.html_attr_value("a'c") #=> #<Escape::HTMLAttrValue: "a'c">
294
- #
295
- # It escapes 4 characters:
296
- # * '&' to '&amp;'
297
- # * '<' to '&lt;'
298
- # * '>' to '&gt;'
299
- # * '"' to '&quot;'
300
- #
301
- def html_attr_value(str)
302
- s = '"' + str.gsub(/[&<>"]/) {|ch| HTML_ATTR_ESCAPE_HASH[ch] } + '"'
303
- HTMLAttrValue.new_no_dup(s)
304
- end
305
- end
@@ -1,135 +0,0 @@
1
-
2
- require 'date'
3
-
4
- module Rudy
5
- module MetaData
6
- class Backup < Storable
7
- include Rudy::AWS
8
-
9
- field :rtype
10
- field :awsid
11
-
12
- field :region
13
- field :zone
14
- field :environment
15
- field :role
16
- field :position
17
- field :path
18
-
19
- field :date
20
- field :time
21
- field :second
22
-
23
- field :unixtime => Integer
24
-
25
- field :size
26
- field :volume
27
-
28
- def initialize
29
- @zone = DEFAULT_ZONE
30
- @region = DEFAULT_REGION
31
- @position = "01"
32
- @rtype = Backup.rtype
33
- time_stamp # initialize time to right now
34
- end
35
-
36
- def self.rtype
37
- 'back'
38
- end
39
-
40
-
41
- def name
42
- time = Time.at(@unixtime)
43
- Backup.generate_name(@zone, @environment, @role, @position, @path, time)
44
- end
45
-
46
- def valid?
47
- #puts(@zone, @environment, @role, @position, @path, @date, @time, @second)
48
- (@zone && @environment && @role && @position && @path && @date && @time && @second)
49
- end
50
-
51
- def time_stamp
52
- #return [@date, @time] if @date && @time
53
- now = Time.now.utc
54
- datetime = Backup.format_timestamp(now).split(Rudy::DELIM)
55
- @unixtime = now.to_i
56
- @date, @time, @second = datetime
57
- end
58
-
59
- def nice_time
60
- return "" unless @date && @time
61
- t = @date.scan(/(\d\d\d\d)(\d\d)(\d\d)/).join('-')
62
- t << " " << @time.scan(/(\d\d)(\d\d)/).join(':')
63
- t
64
- end
65
-
66
- def to_query(more=[], remove=[])
67
- criteria = [:rtype, :zone, :environment, :role, :position, :path, :date, :time, :second, *more]
68
- criteria -= [*remove].flatten
69
- query = "select * from #{Rudy::DOMAIN} where unixtime > '0' "
70
- criteria.each do |n|
71
- query << "and #{n} = '#{self.send(n.to_sym)}'"
72
- end
73
- query << " order by unixtime desc"
74
- end
75
-
76
- def to_s
77
- str = ""
78
- field_names.each do |key|
79
- str << sprintf(" %22s: %s#{$/}", key, self.send(key.to_sym))
80
- end
81
- str
82
- end
83
-
84
- def disk
85
- Disk.generate_name(@zone, @environment, @role, @position, @path)
86
- end
87
-
88
- # 20090224-1813-36
89
- def Backup.format_timestamp(dat)
90
- mon, day, hour, min, sec = [dat.mon, dat.day, dat.hour, dat.min, dat.sec].collect { |v| v.to_s.rjust(2, "0") }
91
- [dat.year, mon, day, Rudy::DELIM, hour, min, Rudy::DELIM, sec].join
92
- end
93
-
94
- # Times are converted to UTC
95
- # back-us-east-1b-stage-app-01-rilli-app-20090224-1813-36
96
- def Backup.generate_name(zon, env, rol, pos, pat, dat, sep=File::SEPARATOR)
97
- raise "The date you provided is not a Time object" unless dat.is_a?(Time)
98
- pos = pos.to_s.rjust 2, '0'
99
- dirs = pat.split sep if pat
100
- dirs.shift while dirs && (dirs[0].nil? || dirs[0].empty?)
101
- timestamp = Backup.format_timestamp(dat.utc)
102
- [rtype, zon, env, rol, pos, dirs, timestamp].flatten.join(Rudy::DELIM)
103
- end
104
-
105
-
106
-
107
-
108
- def to_select
109
-
110
- end
111
-
112
- def save
113
- @@sdb.store(Rudy::DOMAIN, name, self.to_hash, :replace) # Always returns nil
114
- true
115
- end
116
-
117
- def destroy
118
- @@sdb.destroy(Rudy::DOMAIN, name)
119
- true
120
- end
121
-
122
- def refresh
123
- h = @@sdb.get(Rudy::DOMAIN, name) || {}
124
- from_hash(h)
125
- end
126
-
127
- def Backup.get(dname)
128
- h = @@sdb.get(Rudy::DOMAIN, dname) || {}
129
- from_hash(h)
130
- end
131
-
132
- end
133
- end
134
- end
135
-
@@ -1,292 +0,0 @@
1
- #--
2
- # TODO: Handle nested hashes and arrays.
3
- # TODO: to_xml, see: http://codeforpeople.com/lib/ruby/xx/xx-2.0.0/README
4
- # TODO: Rename to Stuffany
5
- #++
6
-
7
- require 'yaml'
8
- require 'fileutils'
9
-
10
-
11
- # Storable makes data available in multiple formats and can
12
- # re-create objects from files. Fields are defined using the
13
- # Storable.field method which tells Storable the order and
14
- # name.
15
- class Storable
16
- unless defined?(SUPPORTED_FORMATS) # We can assume all are defined
17
- VERSION = 5
18
- NICE_TIME_FORMAT = "%Y-%m-%d@%H:%M:%S".freeze
19
- SUPPORTED_FORMATS = [:tsv, :csv, :yaml, :json, :s, :string].freeze
20
- end
21
-
22
- # This value will be used as a default unless provided on-the-fly.
23
- # See SUPPORTED_FORMATS for available values.
24
- attr_reader :format
25
-
26
- # See SUPPORTED_FORMATS for available values
27
- def format=(v)
28
- v &&= v.to_sym
29
- raise "Unsupported format: #{v}" unless SUPPORTED_FORMATS.member?(v)
30
- @format = v
31
- end
32
-
33
- def postprocess
34
- end
35
-
36
- # TODO: from_args([HASH or ordered params])
37
-
38
- # Accepts field definitions in the one of the follow formats:
39
- #
40
- # field :product
41
- # field :product => Integer
42
- #
43
- # The order they're defined determines the order the will be output. The fields
44
- # data is available by the standard accessors, class.product and class.product= etc...
45
- # The value of the field will be cast to the type (if provided) when read from a file.
46
- # The value is not touched when the type is not provided.
47
- def self.field(args={})
48
- # TODO: Examine casting from: http://codeforpeople.com/lib/ruby/fattr/fattr-1.0.3/
49
- args = {args => nil} unless args.is_a? Hash
50
-
51
- args.each_pair do |m,t|
52
-
53
- [[:@@field_names, m], [:@@field_types, t]].each do |tuple|
54
- class_variable_set(tuple[0], []) unless class_variable_defined?(tuple[0])
55
- class_variable_set(tuple[0], class_variable_get(tuple[0]) << tuple[1])
56
- end
57
-
58
- next if method_defined?(m)
59
-
60
- define_method(m) do instance_variable_get("@#{m}") end
61
- define_method("#{m}=") do |val|
62
- instance_variable_set("@#{m}",val)
63
- end
64
- end
65
- end
66
-
67
- # Returns an array of field names defined by self.field
68
- def self.field_names
69
- class_variable_get(:@@field_names)
70
- end
71
- # Returns an array of field names defined by self.field
72
- def field_names
73
- self.class.send(:class_variable_get, :@@field_names)
74
- end
75
- # Returns an array of field types defined by self.field. Fields that did
76
- # not receive a type are set to nil.
77
- def self.field_types
78
- class_variable_get(:@@field_types)
79
- end
80
- # Returns an array of field types defined by self.field. Fields that did
81
- # not receive a type are set to nil.
82
- def field_types
83
- self.class.send(:class_variable_get, :@@field_types)
84
- end
85
-
86
- # Dump the object data to the given format.
87
- def dump(format=nil, with_titles=false)
88
- format &&= format.to_sym
89
- format ||= 's' # as in, to_s
90
- raise "Format not defined (#{format})" unless SUPPORTED_FORMATS.member?(format)
91
- send("to_#{format}", with_titles)
92
- end
93
-
94
- def to_string(*args)
95
- to_s(*args)
96
- end
97
-
98
- # Create a new instance of the object using data from file.
99
- def self.from_file(file_path, format='yaml')
100
- raise "Cannot read file (#{file_path})" unless File.exists?(file_path)
101
- raise "#{self} doesn't support from_#{format}" unless self.respond_to?("from_#{format}")
102
- format = format || File.extname(file_path).tr('.', '')
103
- me = send("from_#{format}", read_file_to_array(file_path))
104
- me.format = format
105
- me
106
- end
107
- # Write the object data to the given file.
108
- def to_file(file_path=nil, with_titles=true)
109
- raise "Cannot store to nil path" if file_path.nil?
110
- format = File.extname(file_path).tr('.', '')
111
- format &&= format.to_sym
112
- format ||= @format
113
- Storable.write_file(file_path, dump(format, with_titles))
114
- end
115
-
116
- # Create a new instance of the object from a hash.
117
- def self.from_hash(from={})
118
- return nil if !from || from.empty?
119
- me = self.new
120
-
121
- fnames = field_names
122
- fnames.each_with_index do |key,index|
123
-
124
- stored_value = from[key] || from[key.to_s] # support for symbol keys and string keys
125
-
126
- # TODO: Correct this horrible implementation (sorry, me. It's just one of those days.)
127
-
128
- if field_types[index] == Array
129
- ((value ||= []) << stored_value).flatten
130
- elsif field_types[index] == Hash
131
-
132
- value = stored_value
133
- else
134
-
135
- # SimpleDB stores attribute shit as lists of values
136
- value = stored_value.first if stored_value.is_a?(Array) && stored_value.size == 1
137
-
138
- if field_types[index] == Time
139
- value = Time.parse(value)
140
- elsif field_types[index] == DateTime
141
- value = DateTime.parse(value)
142
- elsif field_types[index] == TrueClass
143
- value = (value.to_s == "true")
144
- elsif field_types[index] == Float
145
- value = value.to_f
146
- elsif field_types[index] == Integer
147
- value = value.to_i
148
- elsif field_types[index].kind_of?(Storable) && stored_value.is_a?(Hash)
149
- value = field_types[index].from_hash(stored_value)
150
- else
151
- value = (stored_value.is_a?(Array) && stored_value.size == 1) ? stored_value.first : stored_value
152
- end
153
- end
154
-
155
- me.send("#{key}=", value) if self.method_defined?("#{key}=")
156
- end
157
-
158
- me.postprocess
159
-
160
- me
161
- end
162
- # Return the object data as a hash
163
- # +with_titles+ is ignored.
164
- def to_hash(with_titles=true)
165
- tmp = {}
166
- field_names.each do |fname|
167
- tmp[fname] = self.send(fname)
168
- end
169
- tmp
170
- end
171
-
172
- # Create a new instance of the object from YAML.
173
- # +from+ a YAML string split into an array by line.
174
- def self.from_yaml(from=[])
175
- # from is an array of strings
176
- from_str = from.join('')
177
- hash = YAML::load(from_str)
178
- hash = from_hash(hash) if hash.is_a? Hash
179
- hash
180
- end
181
- def to_yaml(with_titles=true)
182
- to_hash.to_yaml
183
- end
184
-
185
- # Create a new instance of the object from a JSON string.
186
- # +from+ a JSON string split into an array by line.
187
- def self.from_json(from=[])
188
- require 'json'
189
- # from is an array of strings
190
- from_str = from.join('')
191
- tmp = JSON::load(from_str)
192
- hash_sym = tmp.keys.inject({}) do |hash, key|
193
- hash[key.to_sym] = tmp[key]
194
- hash
195
- end
196
- hash_sym = from_hash(hash_sym) if hash_sym.is_a? Hash
197
- hash_sym
198
- end
199
- def to_json(with_titles=true)
200
- require 'json'
201
- to_hash.to_json
202
- end
203
-
204
- # Return the object data as a delimited string.
205
- # +with_titles+ specifiy whether to include field names (default: false)
206
- # +delim+ is the field delimiter.
207
- def to_delimited(with_titles=false, delim=',')
208
- values = []
209
- field_names.each do |fname|
210
- values << self.send(fname.to_s) # TODO: escape values
211
- end
212
- output = values.join(delim)
213
- output = field_names.join(delim) << $/ << output if with_titles
214
- output
215
- end
216
- # Return the object data as a tab delimited string.
217
- # +with_titles+ specifiy whether to include field names (default: false)
218
- def to_tsv(with_titles=false)
219
- to_delimited(with_titles, "\t")
220
- end
221
- # Return the object data as a comma delimited string.
222
- # +with_titles+ specifiy whether to include field names (default: false)
223
- def to_csv(with_titles=false)
224
- to_delimited(with_titles, ',')
225
- end
226
- # Create a new instance from tab-delimited data.
227
- # +from+ a JSON string split into an array by line.
228
- def self.from_tsv(from=[])
229
- self.from_delimited(from, "\t")
230
- end
231
- # Create a new instance of the object from comma-delimited data.
232
- # +from+ a JSON string split into an array by line.
233
- def self.from_csv(from=[])
234
- self.from_delimited(from, ',')
235
- end
236
-
237
- # Create a new instance of the object from a delimited string.
238
- # +from+ a JSON string split into an array by line.
239
- # +delim+ is the field delimiter.
240
- def self.from_delimited(from=[],delim=',')
241
- return if from.empty?
242
- # We grab an instance of the class so we can
243
- hash = {}
244
-
245
- fnames = values = []
246
- if (from.size > 1 && !from[1].empty?)
247
- fnames = from[0].chomp.split(delim)
248
- values = from[1].chomp.split(delim)
249
- else
250
- fnames = self.field_names
251
- values = from[0].chomp.split(delim)
252
- end
253
-
254
- fnames.each_with_index do |key,index|
255
- next unless values[index]
256
- hash[key.to_sym] = values[index]
257
- end
258
- hash = from_hash(hash) if hash.is_a? Hash
259
- hash
260
- end
261
-
262
- def self.read_file_to_array(path)
263
- contents = []
264
- return contents unless File.exists?(path)
265
-
266
- open(path, 'r') do |l|
267
- contents = l.readlines
268
- end
269
-
270
- contents
271
- end
272
-
273
- def self.write_file(path, content, flush=true)
274
- write_or_append_file('w', path, content, flush)
275
- end
276
-
277
- def self.append_file(path, content, flush=true)
278
- write_or_append_file('a', path, content, flush)
279
- end
280
-
281
- def self.write_or_append_file(write_or_append, path, content = '', flush = true)
282
- #STDERR.puts "Writing to #{ path }..."
283
- create_dir(File.dirname(path))
284
-
285
- open(path, write_or_append) do |f|
286
- f.puts content
287
- f.flush if flush;
288
- end
289
- File.chmod(0600, path)
290
- end
291
- end
292
-