solutious-rudy 0.4.0

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,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