solutious-rudy 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,68 @@
1
+
2
+ require 'date'
3
+
4
+ module Rudy
5
+ module SCM
6
+ class SVN
7
+ attr_accessor :base_uri
8
+
9
+ def initialize(args={:base => ''})
10
+ @base_uri = args[:base]
11
+ end
12
+
13
+ def create_release(username=nil, msg=nil)
14
+ local_uri, local_revision = local_info
15
+ rtag = generate_release_tag_name(username)
16
+ release_uri = "#{@base_uri}/#{rtag}"
17
+ msg ||= 'Another Release by Rudy!'
18
+ msg.tr!("'", "\\'")
19
+ cmd = "svn copy -m '#{msg}' #{local_uri} #{release_uri}"
20
+
21
+ `#{cmd} 2>&1`
22
+
23
+ release_uri
24
+ end
25
+
26
+ def switch_working_copy(tag)
27
+ raise "Invalid release tag (#{tag})." unless valid_uri?(tag)
28
+ `svn switch #{tag}`
29
+ end
30
+
31
+ # rel-2009-03-05-user-rev
32
+ def generate_release_tag_name(username=nil)
33
+ now = Time.now
34
+ mon = now.mon.to_s.rjust(2, '0')
35
+ day = now.day.to_s.rjust(2, '0')
36
+ rev = "01"
37
+ criteria = ['rel', now.year, mon, day, rev]
38
+ criteria.insert(-2, username) if username
39
+ tag = criteria.join(RUDY_DELIM)
40
+ # Keep incrementing the revision number until we find the next one.
41
+ tag.succ! while (valid_uri?("#{@base_uri}/#{tag}"))
42
+ tag
43
+ end
44
+
45
+ def local_info
46
+ ret = `svn info 2>&1`
47
+ # URL: http://some/uri/path
48
+ # Repository Root: http://some/uri
49
+ # Repository UUID: c5abe49d-53e4-4ea3-9314-89e1e25aa7e1
50
+ # Revision: 921
51
+ ret.scan(/URL: (http:.+?)\s*\n.+Revision: (\d+)/m).flatten
52
+ end
53
+
54
+ def working_copy?(path)
55
+ (File.exists?(File.join(path, '.svn')))
56
+ end
57
+
58
+ def valid_uri?(uri)
59
+ ret = `svn info #{uri} 2>&1` || '' # Valid SVN URIs will return some info
60
+ (ret =~ /Repository UUID/) ? true : false
61
+ end
62
+
63
+ def everything_checked_in?
64
+ `svn diff . 2>&1` == '' # svn diff should return nothing
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,64 @@
1
+
2
+ require 'socket'
3
+ require 'open-uri'
4
+ require 'date'
5
+
6
+ require 'timeout'
7
+
8
+ module Rudy
9
+
10
+ # A motley collection of methods that Rudy loves to call!
11
+ module Utils
12
+ extend self
13
+ include Socket::Constants
14
+
15
+ # Return the external IP address (the one seen by the internet)
16
+ def external_ip_address
17
+ ip = nil
18
+ %w{solutious.com/ip myip.dk/ whatismyip.com }.each do |sponge| # w/ backup
19
+ break unless ip.nil?
20
+ ip = (open("http://#{sponge}") { |f| /([0-9]{1,3}\.){3}[0-9]{1,3}/.match(f.read) }).to_s rescue nil
21
+ end
22
+ ip += "/32" if ip
23
+ ip
24
+ end
25
+
26
+ # Return the local IP address which receives external traffic
27
+ # from: http://coderrr.wordpress.com/2008/05/28/get-your-local-ip-address/
28
+ # NOTE: This <em>does not</em> open a connection to the IP address.
29
+ def internal_ip_address
30
+ # turn off reverse DNS resolution temporarily
31
+ orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true
32
+ ip = UDPSocket.open {|s| s.connect('75.101.137.7', 1); s.addr.last } # Solutious IP
33
+ ip += "/24" if ip
34
+ ip
35
+ ensure
36
+ Socket.do_not_reverse_lookup = orig
37
+ end
38
+
39
+ # Generates a canonical tag name in the form:
40
+ # rudy-2009-12-31-r1
41
+ # where r1 refers to the revision number that day
42
+ def generate_tag(revision=1)
43
+ n = DateTime.now
44
+ y = n.year.to_s.rjust(4, "20")
45
+ m = n.month.to_s.rjust(2, "0")
46
+ d = n.mday.to_s.rjust(2, "0")
47
+ "rudy-%4s-%2s-%2s-r%s" % [y, m, d, revision.to_s.rjust(2, "0")]
48
+ end
49
+
50
+
51
+ def service_available?(host, port, wait=3)
52
+ begin
53
+ status = Timeout::timeout(wait) do
54
+ socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
55
+ sockaddr = Socket.pack_sockaddr_in( port, host )
56
+ socket.connect( sockaddr )
57
+ end
58
+ true
59
+ rescue Errno::EAFNOSUPPORT, Errno::ECONNREFUSED, SocketError, Timeout::Error => ex
60
+ false
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,280 @@
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
+ VERSION = 2
17
+ NICE_TIME_FORMAT = "%Y-%m-%d@%H:%M:%S".freeze unless defined? NICE_TIME_FORMAT
18
+ SUPPORTED_FORMATS = %w{tsv csv yaml json}.freeze unless defined? SUPPORTED_FORMATS
19
+
20
+ # This value will be used as a default unless provided on-the-fly.
21
+ # See SUPPORTED_FORMATS for available values.
22
+ attr_reader :format
23
+
24
+ # See SUPPORTED_FORMATS for available values
25
+ def format=(v)
26
+ raise "Unsupported format: #{v}" unless SUPPORTED_FORMATS.member?(v)
27
+ @format = v
28
+ end
29
+
30
+ # TODO: from_args([HASH or ordered params])
31
+
32
+ def init
33
+ # NOTE: I think this can be removed
34
+ self.class.send(:class_variable_set, :@@field_names, []) unless class_variable_defined?(:@@field_names)
35
+ self.class.send(:class_variable_set, :@@field_types, []) unless class_variable_defined?(:@@field_types)
36
+ end
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
+ # Ditto.
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
+ # Ditto.
81
+ def field_types
82
+ self.class.send(:class_variable_get, :@@field_types)
83
+ end
84
+
85
+ # Dump the object data to the given format.
86
+ def dump(format=nil, with_titles=true)
87
+ format ||= @format
88
+ raise "Format not defined (#{format})" unless SUPPORTED_FORMATS.member?(format)
89
+ send("to_#{format}", with_titles)
90
+ end
91
+
92
+ # Create a new instance of the object using data from file.
93
+ def self.from_file(file_path, format='yaml')
94
+ raise "Cannot read file (#{file_path})" unless File.exists?(file_path)
95
+ raise "#{self} doesn't support from_#{format}" unless self.respond_to?("from_#{format}")
96
+ format = format || File.extname(file_path).tr('.', '')
97
+ me = send("from_#{format}", read_file_to_array(file_path))
98
+ me.format = format
99
+ me
100
+ end
101
+ # Write the object data to the given file.
102
+ def to_file(file_path=nil, with_titles=true)
103
+ raise "Cannot store to nil path" if file_path.nil?
104
+ format = File.extname(file_path).tr('.', '')
105
+ format ||= @format
106
+ Storable.write_file(file_path, dump(format, with_titles))
107
+ end
108
+
109
+ # Create a new instance of the object from a hash.
110
+ def self.from_hash(from={})
111
+ me = self.new
112
+
113
+ return me if !from || from.empty?
114
+
115
+ fnames = field_names
116
+ fnames.each_with_index do |key,index|
117
+
118
+ stored_value = from[key] || from[key.to_s] # support for symbol keys and string keys
119
+
120
+ # TODO: Correct this horrible implementation (sorry, me. It's just one of those days.)
121
+
122
+ if field_types[index] == Array
123
+ ((value ||= []) << stored_value).flatten
124
+ elsif field_types[index] == Hash
125
+ value = stored_value
126
+ else
127
+ # SimpleDB stores attribute shit as lists of values
128
+ value = stored_value.first if stored_value.is_a?(Array) && stored_value.size == 1
129
+
130
+ if field_types[index] == Time
131
+ value = Time.parse(value)
132
+ elsif field_types[index] == DateTime
133
+ value = DateTime.parse(value)
134
+ elsif field_types[index] == TrueClass
135
+ value = (value.to_s == "true")
136
+ elsif field_types[index] == Float
137
+ value = value.to_f
138
+ elsif field_types[index] == Integer
139
+ value = value.to_i
140
+ else
141
+ value = value.first if value.is_a?(Array) && value.size == 1 # I
142
+ end
143
+ end
144
+
145
+ me.send("#{key}=", value) if self.method_defined?("#{key}=")
146
+ end
147
+
148
+ me
149
+ end
150
+ # Return the object data as a hash
151
+ # +with_titles+ is ignored.
152
+ def to_hash(with_titles=true)
153
+ tmp = {}
154
+ field_names.each do |fname|
155
+ tmp[fname] = self.send(fname)
156
+ end
157
+ tmp
158
+ end
159
+
160
+ # Create a new instance of the object from YAML.
161
+ # +from+ a YAML string split into an array by line.
162
+ def self.from_yaml(from=[])
163
+ # from is an array of strings
164
+ from_str = from.join('')
165
+ hash = YAML::load(from_str)
166
+ hash = from_hash(hash) if hash.is_a? Hash
167
+ hash
168
+ end
169
+ def to_yaml(with_titles=true)
170
+ to_hash.to_yaml
171
+ end
172
+
173
+ # Create a new instance of the object from a JSON string.
174
+ # +from+ a JSON string split into an array by line.
175
+ def self.from_json(from=[])
176
+ require 'json'
177
+ # from is an array of strings
178
+ from_str = from.join('')
179
+ tmp = JSON::load(from_str)
180
+ hash_sym = tmp.keys.inject({}) do |hash, key|
181
+ hash[key.to_sym] = tmp[key]
182
+ hash
183
+ end
184
+ hash_sym = from_hash(hash_sym) if hash_sym.is_a? Hash
185
+ hash_sym
186
+ end
187
+ def to_json(with_titles=true)
188
+ require 'json'
189
+ to_hash.to_json
190
+ end
191
+
192
+ # Return the object data as a delimited string.
193
+ # +with_titles+ specifiy whether to include field names (default: false)
194
+ # +delim+ is the field delimiter.
195
+ def to_delimited(with_titles=false, delim=',')
196
+ values = []
197
+ field_names.each do |fname|
198
+ values << self.send(fname.to_s) # TODO: escape values
199
+ end
200
+ output = values.join(delim)
201
+ output = field_names.join(delim) << $/ << output if with_titles
202
+ output
203
+ end
204
+ # Return the object data as a tab delimited string.
205
+ # +with_titles+ specifiy whether to include field names (default: false)
206
+ def to_tsv(with_titles=false)
207
+ to_delimited(with_titles, "\t")
208
+ end
209
+ # Return the object data as a comma delimited string.
210
+ # +with_titles+ specifiy whether to include field names (default: false)
211
+ def to_csv(with_titles=false)
212
+ to_delimited(with_titles, ',')
213
+ end
214
+ # Create a new instance from tab-delimited data.
215
+ # +from+ a JSON string split into an array by line.
216
+ def self.from_tsv(from=[])
217
+ self.from_delimited(from, "\t")
218
+ end
219
+ # Create a new instance of the object from comma-delimited data.
220
+ # +from+ a JSON string split into an array by line.
221
+ def self.from_csv(from=[])
222
+ self.from_delimited(from, ',')
223
+ end
224
+
225
+ # Create a new instance of the object from a delimited string.
226
+ # +from+ a JSON string split into an array by line.
227
+ # +delim+ is the field delimiter.
228
+ def self.from_delimited(from=[],delim=',')
229
+ return if from.empty?
230
+ # We grab an instance of the class so we can
231
+ hash = {}
232
+
233
+ fnames = values = []
234
+ if (from.size > 1 && !from[1].empty?)
235
+ fnames = from[0].chomp.split(delim)
236
+ values = from[1].chomp.split(delim)
237
+ else
238
+ fnames = self.field_names
239
+ values = from[0].chomp.split(delim)
240
+ end
241
+
242
+ fnames.each_with_index do |key,index|
243
+ next unless values[index]
244
+ hash[key.to_sym] = values[index]
245
+ end
246
+ hash = from_hash(hash) if hash.is_a? Hash
247
+ hash
248
+ end
249
+
250
+ def self.read_file_to_array(path)
251
+ contents = []
252
+ return contents unless File.exists?(path)
253
+
254
+ open(path, 'r') do |l|
255
+ contents = l.readlines
256
+ end
257
+
258
+ contents
259
+ end
260
+
261
+ def self.write_file(path, content, flush=true)
262
+ write_or_append_file('w', path, content, flush)
263
+ end
264
+
265
+ def self.append_file(path, content, flush=true)
266
+ write_or_append_file('a', path, content, flush)
267
+ end
268
+
269
+ def self.write_or_append_file(write_or_append, path, content = '', flush = true)
270
+ #STDERR.puts "Writing to #{ path }..."
271
+ create_dir(File.dirname(path))
272
+
273
+ open(path, write_or_append) do |f|
274
+ f.puts content
275
+ f.flush if flush;
276
+ end
277
+ File.chmod(0600, path)
278
+ end
279
+ end
280
+
@@ -0,0 +1,40 @@
1
+
2
+ require 'ostruct'
3
+
4
+ module Tryouts
5
+
6
+ def before(&b)
7
+ b.call
8
+ end
9
+ def after(&b)
10
+ at_exit &b
11
+ end
12
+
13
+
14
+ # tryout :name do
15
+ # ...
16
+ # end
17
+ def tryout(name, &b)
18
+ puts "Running#{@poop}: #{name}"
19
+ begin
20
+ b.call
21
+ puts $/*2
22
+ sleep 1
23
+ rescue Interrupt
24
+ end
25
+ end
26
+
27
+ # Ignore everything
28
+ def xtryout(name, &b)
29
+ end
30
+
31
+ # Is this wacky syntax useful for anything?
32
+ # t2 :set .
33
+ # run = "poop"
34
+ def t2(*args)
35
+ OpenStruct.new
36
+ end
37
+
38
+ end
39
+
40
+ include Tryouts