stella 0.6.0 → 0.7.0.002
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.
- data/CHANGES.txt +7 -15
- data/LICENSE.txt +1 -1
- data/README.rdoc +93 -63
- data/Rakefile +32 -42
- data/bin/stella +138 -0
- data/examples/basic/listing_ids.csv +7 -0
- data/examples/basic/plan.rb +71 -0
- data/lib/stella/cli.rb +66 -0
- data/lib/stella/client.rb +199 -0
- data/lib/stella/config.rb +87 -0
- data/lib/stella/data/http/body.rb +15 -0
- data/lib/stella/data/http/request.rb +116 -0
- data/lib/stella/data/http/response.rb +92 -0
- data/lib/stella/data/http.rb +2 -257
- data/lib/stella/data.rb +85 -0
- data/lib/stella/dsl.rb +5 -0
- data/lib/stella/engine/functional.rb +39 -0
- data/lib/stella/engine/load.rb +106 -0
- data/lib/stella/engine.rb +55 -0
- data/lib/stella/exceptions.rb +15 -0
- data/lib/stella/guidelines.rb +18 -0
- data/lib/stella/mixins.rb +2 -0
- data/lib/stella/stats.rb +3 -7
- data/lib/stella/testplan/stats.rb +26 -0
- data/lib/stella/testplan/usecase.rb +67 -0
- data/lib/stella/testplan.rb +95 -220
- data/lib/{util → stella/utils}/httputil.rb +0 -0
- data/lib/stella/utils.rb +126 -0
- data/lib/stella/version.rb +15 -0
- data/lib/stella.rb +58 -104
- data/lib/threadify.rb +0 -6
- data/stella.gemspec +43 -49
- data/support/example_webapp.rb +246 -0
- data/support/useragents.txt +75 -0
- metadata +68 -32
- data/bin/example_test.rb +0 -82
- data/bin/example_webapp.rb +0 -63
- data/lib/logger.rb +0 -79
- data/lib/stella/clients.rb +0 -161
- data/lib/stella/command/base.rb +0 -20
- data/lib/stella/command/form.rb +0 -36
- data/lib/stella/command/get.rb +0 -44
- data/lib/stella/common.rb +0 -53
- data/lib/stella/crypto.rb +0 -88
- data/lib/stella/data/domain.rb +0 -82
- data/lib/stella/environment.rb +0 -66
- data/lib/stella/functest.rb +0 -105
- data/lib/stella/loadtest.rb +0 -186
- data/lib/stella/testrunner.rb +0 -64
- data/lib/storable.rb +0 -280
- data/lib/timeunits.rb +0 -65
- data/tryouts/drb/drb_test.rb +0 -65
- data/tryouts/drb/open4.rb +0 -19
- data/tryouts/drb/slave.rb +0 -27
- data/tryouts/oo_tryout.rb +0 -30
@@ -0,0 +1,67 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Stella
|
4
|
+
class Testplan
|
5
|
+
|
6
|
+
class Usecase
|
7
|
+
include Gibbler::Complex
|
8
|
+
attr_accessor :desc
|
9
|
+
attr_accessor :requests
|
10
|
+
attr_writer :ratio
|
11
|
+
attr_accessor :resources
|
12
|
+
attr_accessor :base_path
|
13
|
+
|
14
|
+
def initialize(&blk)
|
15
|
+
@requests, @resources = [], {}
|
16
|
+
instance_eval &blk unless blk.nil?
|
17
|
+
end
|
18
|
+
|
19
|
+
def desc(*args)
|
20
|
+
@desc = args.first unless args.empty?
|
21
|
+
@desc
|
22
|
+
end
|
23
|
+
|
24
|
+
def resource(name, value=nil)
|
25
|
+
@resources[name] = value unless value.nil?
|
26
|
+
@resources[name]
|
27
|
+
end
|
28
|
+
|
29
|
+
def ratio
|
30
|
+
r = (@ratio || 0).to_f
|
31
|
+
r = r/100 if r > 1
|
32
|
+
r
|
33
|
+
end
|
34
|
+
|
35
|
+
# Reads the contents of the file <tt>path</tt> (the current working
|
36
|
+
# directory is assumed to be the same directory containing the test plan).
|
37
|
+
def file(path)
|
38
|
+
path = File.join(@base_path, path) if @base_path
|
39
|
+
File.read(path)
|
40
|
+
end
|
41
|
+
|
42
|
+
def list(path)
|
43
|
+
file(path).split $/
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_request(meth, *args, &blk)
|
47
|
+
req = Stella::Data::HTTP::Request.new meth.to_s.upcase, args[0], &blk
|
48
|
+
req.desc = args[1] if args.size > 1 # Description is optional
|
49
|
+
Stella.ld req
|
50
|
+
@requests << req
|
51
|
+
req
|
52
|
+
end
|
53
|
+
def get(*args, &blk); add_request :get, *args, &blk; end
|
54
|
+
def put(*args, &blk); add_request :put, *args, &blk; end
|
55
|
+
def post(*args, &blk); add_request :post, *args, &blk; end
|
56
|
+
def head(*args, &blk); add_request :head, *args, &blk; end
|
57
|
+
def delete(*args, &blk); add_request :delete, *args, &blk; end
|
58
|
+
|
59
|
+
def xget(*args, &blk); Stella.ld "Skipping get" end
|
60
|
+
def xput(*args, &blk); Stella.ld "Skipping put" end
|
61
|
+
def xpost(*args, &blk); Stella.ld "Skipping post" end
|
62
|
+
def xhead(*args, &blk); Stella.ld "Skipping head" end
|
63
|
+
def xdelete(*args, &blk); Stella.ld "Skipping delete" end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/stella/testplan.rb
CHANGED
@@ -1,237 +1,112 @@
|
|
1
|
+
require 'stella/testplan/usecase'
|
2
|
+
require 'stella/testplan/stats'
|
1
3
|
|
2
4
|
module Stella
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
attr_accessor :wait
|
8
|
-
def initialize(action, times=1, wait=1)
|
9
|
-
@action = action
|
10
|
-
@times = times
|
11
|
-
@wait = wait
|
12
|
-
end
|
13
|
-
end
|
5
|
+
class Testplan
|
6
|
+
include Gibbler::Complex
|
7
|
+
|
8
|
+
class WackyRatio < Stella::Error
|
14
9
|
end
|
15
|
-
end
|
16
|
-
|
17
|
-
module Stella
|
18
|
-
|
19
|
-
class TestPlan
|
20
|
-
# The name of the testplan.
|
21
|
-
attr_accessor :name
|
22
|
-
# A brief description of this testplan
|
23
|
-
attr_accessor :description
|
24
|
-
# Used as the default protocol for the testplan. One of: http, https
|
25
|
-
attr_accessor :protocol
|
26
|
-
# A Stella::TestPlan::Auth object
|
27
|
-
attr_accessor :auth
|
28
|
-
# An array of Stella::TestPlan::Request objects representing all "primary" requests
|
29
|
-
# for the given test plan (an html page for example). Each primary Request object can have an array of "auxilliary"
|
30
|
-
# requests which represent dependencies for that resource (javascript, images, callbacks, etc...).
|
31
|
-
attr_accessor :requests
|
32
|
-
|
33
|
-
def initialize(name=:anonymous)
|
34
|
-
@name = name
|
35
|
-
@requests = []
|
36
|
-
@servers = []
|
37
|
-
@protocol = "http"
|
38
|
-
end
|
39
|
-
|
40
10
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
11
|
+
|
12
|
+
attr_accessor :usecases
|
13
|
+
attr_accessor :base_path
|
14
|
+
attr_accessor :desc
|
15
|
+
attr_reader :stats
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@desc, @usecases = "Stella's plan", []
|
19
|
+
@testplan_current_ratio = 0
|
20
|
+
@stats = Stella::Testplan::Stats.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.load_file(path)
|
24
|
+
conf = File.read path
|
25
|
+
plan = Stella::Testplan.new
|
26
|
+
plan.base_path = File.dirname path
|
27
|
+
# eval so the DSL code can be executed in this namespace.
|
28
|
+
plan.instance_eval conf
|
29
|
+
plan
|
30
|
+
end
|
31
|
+
|
32
|
+
def check!
|
33
|
+
# Adjust ratios if necessary
|
34
|
+
needy = @usecases.select { |u| u.ratio == -1 }
|
35
|
+
needy.each do |u|
|
36
|
+
u.ratio = (remaining_ratio / needy.size).to_f
|
47
37
|
end
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
raise "That is not an instance of Stella::Data::HTTPRequest" unless req.is_a? Stella::Data::HTTPRequest
|
55
|
-
@requests << req
|
38
|
+
# Give usecases a name if necessary
|
39
|
+
@usecases.each_with_index { |uc,i| uc.desc ||= "Usecase ##{i+1}" }
|
40
|
+
if @testplan_current_ratio > 1.0
|
41
|
+
msg = "Usecase ratio cannot be higher than 1.0"
|
42
|
+
msg << " (#{@testplan_current_ratio})"
|
43
|
+
raise WackyRatio, msg
|
56
44
|
end
|
45
|
+
end
|
57
46
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
47
|
+
def usecase(*args, &blk)
|
48
|
+
return @usecases if args.empty?
|
49
|
+
ratio, name = nil,nil
|
50
|
+
ratio, name = args[0], args[1] if args[0].is_a?(Numeric)
|
51
|
+
ratio, name = args[1], args[0] if args[0].is_a?(String)
|
52
|
+
uc = Stella::Testplan::Usecase.new
|
53
|
+
uc.base_path = @base_path
|
54
|
+
uc.instance_eval &blk
|
55
|
+
uc.ratio, uc.desc = (ratio || -1).to_f, name
|
56
|
+
@testplan_current_ratio += uc.ratio if uc.ratio > 0
|
57
|
+
add_usecase uc
|
58
|
+
end
|
59
|
+
def xusecase(*args, &blk); Stella.ld "Skipping usecase"; end
|
60
|
+
|
61
|
+
def add_usecase(uc)
|
62
|
+
Stella.ld "Usecase: #{uc.desc}"
|
63
|
+
@usecases << uc
|
64
|
+
uc
|
65
|
+
end
|
66
|
+
|
67
|
+
def desc(*args)
|
68
|
+
@desc = args.first unless args.empty?
|
69
|
+
@desc
|
70
|
+
end
|
64
71
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
@protocol = uri.scheme unless @protocol
|
77
|
-
rescue => ex
|
78
|
-
Stella.fatal(ex)
|
72
|
+
def pretty
|
73
|
+
str = []
|
74
|
+
str << " %-50s ".att(:reverse) % [@desc]
|
75
|
+
@usecases.each_with_index do |uc,i|
|
76
|
+
description = uc.desc || "Usecase ##{i+1}"
|
77
|
+
str << " %s (%s)".bright % [description, uc.ratio]
|
78
|
+
requests = uc.requests.each do |r|
|
79
|
+
str << " %-35s %s" % ["#{r.desc}:", r]
|
80
|
+
if Stella.loglev > 2
|
81
|
+
[:wait].each { |i| str << " %s: %s" % [i, r.send(i)] }
|
82
|
+
end
|
79
83
|
end
|
80
84
|
end
|
81
|
-
|
82
|
-
|
85
|
+
str.join($/)
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
def remaining_ratio
|
90
|
+
1.0 - @testplan_current_ratio
|
83
91
|
end
|
84
|
-
|
85
92
|
|
86
93
|
end
|
94
|
+
end
|
87
95
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
def testplan(name, &define)
|
103
|
-
@plans ||= {}
|
104
|
-
@current_plan = @plans[name] = Stella::TestPlan.new(name)
|
105
|
-
define.call if define
|
106
|
-
end
|
107
|
-
|
108
|
-
def plans
|
109
|
-
@plans
|
110
|
-
end
|
111
|
-
|
112
|
-
def repeat(*args)
|
113
|
-
raise "Repeat format does not look like a hash" unless args.first.is_a?(Hash)
|
114
|
-
response_handler = Stella::TestPlan::ResponseHandler.new(:repeat)
|
115
|
-
[:times, :wait].each do |att|
|
116
|
-
response_handler.send("#{att}=", args.first[att])
|
117
|
-
end
|
118
|
-
|
119
|
-
response_handler
|
120
|
-
end
|
121
|
-
|
122
|
-
def body(*args)
|
123
|
-
|
124
|
-
raise "current_plan is not a valid testplan: #{@current_plan}" unless @current_plan.is_a? Stella::TestPlan
|
125
|
-
|
126
|
-
# NOTE: @current_request must be set in the calling namespace
|
127
|
-
# before this method is called. See: make_request
|
128
|
-
raise "current_request is not a valid request" unless @current_request.is_a? Stella::Data::HTTPRequest
|
129
|
-
|
130
|
-
param, content_type, content = args if args.size == 3
|
131
|
-
param, content = args if args.size == 2
|
132
|
-
content = args.first if args.size == 1
|
133
|
-
|
134
|
-
@current_request.add_body(content, param, content_type)
|
135
|
-
end
|
136
|
-
|
137
|
-
def name(*args)
|
138
|
-
raise "current_plan is not a valid testplan: #{@current_plan}" unless @current_plan.is_a? Stella::TestPlan
|
139
|
-
|
140
|
-
# NOTE: @current_request must be set in the calling namespace
|
141
|
-
# before this method is called. See: make_request
|
142
|
-
raise "current_request is not a valid request" unless @current_request.is_a? Stella::Data::HTTPRequest
|
143
|
-
@current_request.name = args.first
|
144
|
-
end
|
145
|
-
|
146
|
-
|
147
|
-
def response(*args, &b)
|
148
|
-
raise "current_plan is not a valid testplan" unless @current_plan.is_a? Stella::TestPlan
|
149
|
-
|
150
|
-
# NOTE: @current_request must be set in the calling namespace
|
151
|
-
# before this method is called. See: make_request
|
152
|
-
raise "current_request is not a valid request" unless @current_request.is_a? Stella::Data::HTTPRequest
|
153
|
-
|
154
|
-
@current_request.add_response_handler(*args, &b)
|
155
|
-
end
|
156
|
-
private :response
|
157
|
-
|
158
|
-
# Stella::Data::HTTPRequest#add_ methods
|
159
|
-
[:header, :param].each do |method_name|
|
160
|
-
eval <<-RUBY, binding, '(Stella::DSL::TestPlan)', 1
|
161
|
-
def #{method_name}(*args, &b)
|
162
|
-
raise "current_plan is not a valid testplan" unless @current_plan.is_a? Stella::TestPlan
|
163
|
-
|
164
|
-
# NOTE: @current_request must be set in the calling namespace
|
165
|
-
# before this method is called. See: make_request
|
166
|
-
raise "current_request is not a valid request" unless @current_request.is_a? Stella::Data::HTTPRequest
|
167
|
-
|
168
|
-
@current_request.add_#{method_name}(*args, &b)
|
169
|
-
end
|
170
|
-
private :#{method_name}
|
171
|
-
RUBY
|
172
|
-
end
|
173
|
-
|
174
|
-
# TestPlan#= methods
|
175
|
-
[:proxy, :auth, :base_uri, :desc, :description].each do |method_name|
|
176
|
-
eval <<-RUBY, binding, '(Stella::DSL::TestPlan)', 1
|
177
|
-
def #{method_name}(*args)
|
178
|
-
return unless @current_plan.is_a? Stella::TestPlan
|
179
|
-
@current_plan.#{method_name}=(args)
|
180
|
-
end
|
181
|
-
private :#{method_name}
|
182
|
-
RUBY
|
183
|
-
end
|
184
|
-
|
185
|
-
# = methods
|
186
|
-
[:protocol].each do |method_name|
|
187
|
-
eval <<-RUBY, binding, '(Stella::DSL::TestPlan)', 1
|
188
|
-
def #{method_name}(val)
|
189
|
-
return unless @current_plan.is_a? Stella::TestPlan
|
190
|
-
@current_plan.#{method_name}=(val.to_s)
|
191
|
-
end
|
192
|
-
private :#{method_name}
|
193
|
-
RUBY
|
194
|
-
end
|
195
|
-
|
196
|
-
def post(uri, &define)
|
197
|
-
make_request(:POST, uri, &define)
|
198
|
-
end
|
199
|
-
def xpost(*args); end;
|
200
|
-
def xget(*args); end;
|
201
|
-
|
202
|
-
def get(uri, &define)
|
203
|
-
make_request(:GET, uri, &define)
|
204
|
-
end
|
205
|
-
|
206
|
-
private
|
207
|
-
|
208
|
-
def make_request(method, uri, &define)
|
209
|
-
return unless @current_plan.is_a? Stella::TestPlan
|
210
|
-
req = Stella::Data::HTTPRequest.new(uri, method.to_s.upcase)
|
211
|
-
@current_plan.add_request req
|
212
|
-
index = @current_plan.requests.size
|
213
|
-
method_name = :"#{index} #{req.http_method} #{req.uri}"
|
214
|
-
|
215
|
-
req_method = Proc.new {
|
216
|
-
# These instance variables are very important. The bring in the context
|
217
|
-
# when the request method is called in the testrunner class. We know what
|
218
|
-
# the current plan is while we're executing the DSL blocks to define the
|
219
|
-
# request. The response block however, is called only after a require request
|
220
|
-
# is made. We set these instance variables so that the response block will
|
221
|
-
# know what request it's associated too.
|
222
|
-
instance_variable_set('@current_plan', @current_plan)
|
223
|
-
instance_variable_set('@current_request', req)
|
224
|
-
define.call if define
|
225
|
-
req
|
226
|
-
}
|
227
|
-
metaclass.instance_eval do
|
228
|
-
define_method(method_name, &req_method)
|
229
|
-
end
|
230
|
-
|
231
|
-
end
|
232
|
-
|
96
|
+
__END__
|
97
|
+
# instance_exec for Ruby 1.8 written by Mauricio Fernandez
|
98
|
+
# http://eigenclass.org/hiki/instance_exec
|
99
|
+
if RUBY_VERSION =~ /1.8/
|
100
|
+
module InstanceExecHelper; end
|
101
|
+
include InstanceExecHelper
|
102
|
+
def instance_exec(*args, &block) # !> method redefined; discarding old instance_exec
|
103
|
+
mname = "__instance_exec_#{Thread.current.object_id.abs}_#{object_id.abs}"
|
104
|
+
InstanceExecHelper.module_eval{ define_method(mname, &block) }
|
105
|
+
begin
|
106
|
+
ret = send(mname, *args)
|
107
|
+
ensure
|
108
|
+
InstanceExecHelper.module_eval{ undef_method(mname) } rescue nil
|
233
109
|
end
|
110
|
+
ret
|
234
111
|
end
|
235
112
|
end
|
236
|
-
|
237
|
-
|
File without changes
|
data/lib/stella/utils.rb
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
|
2
|
+
require 'socket'
|
3
|
+
require 'open-uri'
|
4
|
+
require 'date'
|
5
|
+
|
6
|
+
require 'timeout'
|
7
|
+
|
8
|
+
module Stella
|
9
|
+
|
10
|
+
# A motley collection of methods that Stella 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
|
+
begin
|
19
|
+
%w{solutious.heroku.com/ip}.each do |sponge|
|
20
|
+
ipstr = Net::HTTP.get(URI.parse("http://#{sponge}")) || ''
|
21
|
+
ip = /([0-9]{1,3}\.){3}[0-9]{1,3}/.match(ipstr).to_s
|
22
|
+
break if ip && !ip.empty?
|
23
|
+
end
|
24
|
+
rescue SocketError, Errno::ETIMEDOUT => ex
|
25
|
+
Stella.le "Connection Error. Check your internets!"
|
26
|
+
end
|
27
|
+
ip
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return the local IP address which receives external traffic
|
31
|
+
# from: http://coderrr.wordpress.com/2008/05/28/get-your-local-ip-address/
|
32
|
+
# NOTE: This <em>does not</em> open a connection to the IP address.
|
33
|
+
def internal_ip_address
|
34
|
+
# turn off reverse DNS resolution temporarily
|
35
|
+
orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true
|
36
|
+
ip = UDPSocket.open {|s| s.connect('75.101.137.7', 1); s.addr.last } # Solutious IP
|
37
|
+
ip
|
38
|
+
ensure
|
39
|
+
Socket.do_not_reverse_lookup = orig
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
# <tt>require</tt> a glob of files.
|
44
|
+
# * +path+ is a list of path elements which is sent to File.join
|
45
|
+
# and then to Dir.glob. The list of files found are sent to require.
|
46
|
+
# Nothing is returned but LoadError exceptions are caught. The message
|
47
|
+
# is printed to STDERR and the program exits with 7.
|
48
|
+
def require_glob(*path)
|
49
|
+
begin
|
50
|
+
Dir.glob(File.join(*path.flatten)).each do |path|
|
51
|
+
require path
|
52
|
+
end
|
53
|
+
rescue LoadError => ex
|
54
|
+
puts "Error: #{ex.message}"
|
55
|
+
exit 7
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Checks whether something is listening to a socket.
|
60
|
+
# * +host+ A hostname
|
61
|
+
# * +port+ The port to check
|
62
|
+
# * +wait+ The number of seconds to wait for before timing out.
|
63
|
+
#
|
64
|
+
# Returns true if +host+ allows a socket connection on +port+.
|
65
|
+
# Returns false if one of the following exceptions is raised:
|
66
|
+
# Errno::EAFNOSUPPORT, Errno::ECONNREFUSED, SocketError, Timeout::Error
|
67
|
+
#
|
68
|
+
def service_available?(host, port, wait=3)
|
69
|
+
if Stella.sysinfo.vm == :java
|
70
|
+
begin
|
71
|
+
iadd = Java::InetSocketAddress.new host, port
|
72
|
+
socket = Java::Socket.new
|
73
|
+
socket.connect iadd, wait * 1000 # milliseconds
|
74
|
+
success = !socket.isClosed && socket.isConnected
|
75
|
+
rescue NativeException => ex
|
76
|
+
puts ex.message, ex.backtrace if Stella.debug?
|
77
|
+
false
|
78
|
+
end
|
79
|
+
else
|
80
|
+
begin
|
81
|
+
status = Timeout::timeout(wait) do
|
82
|
+
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
|
83
|
+
sockaddr = Socket.pack_sockaddr_in( port, host )
|
84
|
+
socket.connect( sockaddr )
|
85
|
+
end
|
86
|
+
true
|
87
|
+
rescue Errno::EAFNOSUPPORT, Errno::ECONNREFUSED, SocketError, Timeout::Error => ex
|
88
|
+
puts ex.class, ex.message, ex.backtrace if Stella.debug?
|
89
|
+
false
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# A basic file writer
|
95
|
+
def write_to_file(filename, content, mode, chmod=600)
|
96
|
+
mode = (mode == :append) ? 'a' : 'w'
|
97
|
+
f = File.open(filename,mode)
|
98
|
+
f.puts content
|
99
|
+
f.close
|
100
|
+
return unless Stella.sysinfo.os == :unix
|
101
|
+
raise "Provided chmod is not a Fixnum (#{chmod})" unless chmod.is_a?(Fixnum)
|
102
|
+
File.chmod(chmod, filename)
|
103
|
+
end
|
104
|
+
|
105
|
+
#
|
106
|
+
# Generates a string of random alphanumeric characters.
|
107
|
+
# * +len+ is the length, an Integer. Default: 8
|
108
|
+
# * +safe+ in safe-mode, ambiguous characters are removed (default: true):
|
109
|
+
# i l o 1 0
|
110
|
+
def strand( len=8, safe=true )
|
111
|
+
chars = ("a".."z").to_a + ("0".."9").to_a
|
112
|
+
chars.delete_if { |v| %w(i l o 1 0).member?(v) } if safe
|
113
|
+
str = ""
|
114
|
+
1.upto(len) { |i| str << chars[rand(chars.size-1)] }
|
115
|
+
str
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns +str+ with the leading indentation removed.
|
119
|
+
# Stolen from http://github.com/mynyml/unindent/ because it was better.
|
120
|
+
def noindent(str)
|
121
|
+
indent = str.split($/).each {|line| !line.strip.empty? }.map {|line| line.index(/[^\s]/) }.compact.min
|
122
|
+
str.gsub(/^[[:blank:]]{#{indent}}/, '')
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Stella
|
4
|
+
module Version
|
5
|
+
unless defined?(MAJOR)
|
6
|
+
MAJOR = 0.freeze
|
7
|
+
MINOR = 7.freeze
|
8
|
+
TINY = 0.freeze
|
9
|
+
PATCH = '001'.freeze
|
10
|
+
end
|
11
|
+
def self.to_s; [MAJOR, MINOR, TINY].join('.'); end
|
12
|
+
def self.to_f; self.to_s.to_f; end
|
13
|
+
def self.patch; PATCH; end
|
14
|
+
end
|
15
|
+
end
|