solutious-stella 0.5.5 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +39 -2
- data/LICENSE.txt +19 -0
- data/README.rdoc +85 -0
- data/Rakefile +54 -59
- data/bin/example_test.rb +82 -0
- data/bin/example_webapp.rb +63 -0
- data/lib/{stella/logger.rb → logger.rb} +6 -11
- data/lib/stella.rb +76 -58
- data/lib/stella/clients.rb +161 -0
- data/lib/stella/command/base.rb +4 -24
- data/lib/stella/command/form.rb +36 -0
- data/lib/stella/command/get.rb +44 -0
- data/lib/stella/common.rb +53 -0
- data/lib/stella/crypto.rb +88 -0
- data/lib/stella/data/domain.rb +2 -2
- data/lib/stella/data/http.rb +164 -36
- data/lib/stella/environment.rb +66 -0
- data/lib/stella/functest.rb +105 -0
- data/lib/stella/loadtest.rb +186 -0
- data/lib/{utils → stella}/stats.rb +16 -20
- data/lib/stella/testplan.rb +237 -0
- data/lib/stella/testrunner.rb +64 -0
- data/lib/storable.rb +280 -0
- data/lib/threadify.rb +171 -0
- data/lib/timeunits.rb +65 -0
- data/lib/util/httputil.rb +266 -0
- data/stella.gemspec +69 -0
- data/tryouts/drb/drb_test.rb +65 -0
- data/tryouts/drb/open4.rb +19 -0
- data/tryouts/drb/slave.rb +27 -0
- data/tryouts/oo_tryout.rb +30 -0
- metadata +39 -107
- data/README.textile +0 -162
- data/bin/stella +0 -12
- data/bin/stella.bat +0 -12
- data/lib/daemonize.rb +0 -56
- data/lib/pcaplet.rb +0 -180
- data/lib/stella/adapter/ab.rb +0 -337
- data/lib/stella/adapter/base.rb +0 -106
- data/lib/stella/adapter/httperf.rb +0 -305
- data/lib/stella/adapter/pcap_watcher.rb +0 -221
- data/lib/stella/adapter/proxy_watcher.rb +0 -76
- data/lib/stella/adapter/siege.rb +0 -341
- data/lib/stella/cli.rb +0 -258
- data/lib/stella/cli/agents.rb +0 -73
- data/lib/stella/cli/base.rb +0 -55
- data/lib/stella/cli/language.rb +0 -18
- data/lib/stella/cli/localtest.rb +0 -78
- data/lib/stella/cli/sysinfo.rb +0 -16
- data/lib/stella/cli/watch.rb +0 -278
- data/lib/stella/command/localtest.rb +0 -358
- data/lib/stella/response.rb +0 -85
- data/lib/stella/storable.rb +0 -201
- data/lib/stella/support.rb +0 -276
- data/lib/stella/sysinfo.rb +0 -257
- data/lib/stella/test/definition.rb +0 -79
- data/lib/stella/test/run/summary.rb +0 -70
- data/lib/stella/test/stats.rb +0 -114
- data/lib/stella/text.rb +0 -64
- data/lib/stella/text/resource.rb +0 -38
- data/lib/utils/crypto-key.rb +0 -84
- data/lib/utils/domainutil.rb +0 -47
- data/lib/utils/escape.rb +0 -302
- data/lib/utils/fileutil.rb +0 -78
- data/lib/utils/httputil.rb +0 -266
- data/lib/utils/mathutil.rb +0 -15
- data/lib/utils/textgraph.rb +0 -267
- data/lib/utils/timerutil.rb +0 -58
- data/lib/win32/Console.rb +0 -970
- data/lib/win32/Console/ANSI.rb +0 -305
- data/support/kvm.h +0 -91
- data/support/ruby-pcap-takuma-notes.txt +0 -19
- data/support/ruby-pcap-takuma-patch.txt +0 -30
- data/support/text/en.yaml +0 -80
- data/support/text/nl.yaml +0 -7
- data/support/useragents.txt +0 -75
- data/tests/01-util_test.rb +0 -0
- data/tests/02-stella-util_test.rb +0 -42
- data/tests/10-stella_test.rb +0 -104
- data/tests/11-stella-storable_test.rb +0 -68
- data/tests/60-stella-command_test.rb +0 -248
- data/tests/80-stella-cli_test.rb +0 -45
- data/tests/spec-helper.rb +0 -31
data/lib/timeunits.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# From: http://codeforpeople.com/lib/ruby/timeunits/timeunits-0.0.2/lib/timeunits.rb
|
2
|
+
# ... with fix for Ruby 1.9.1
|
3
|
+
|
4
|
+
unless $__timeunits__
|
5
|
+
$__timeunits__ = File.expand_path __FILE__
|
6
|
+
|
7
|
+
class Time
|
8
|
+
module Units
|
9
|
+
VERSION = "0.0.3" # Changed from 0.0.1 (should have been 0.0.2)
|
10
|
+
|
11
|
+
def __less__() "/" end
|
12
|
+
def __more__() "*" end
|
13
|
+
def microseconds() Float(self.send(__more__,(10 ** -6))) end
|
14
|
+
def milliseconds() Float(self.send(__more__,(10 ** -3))) end
|
15
|
+
def seconds() self end
|
16
|
+
def minutes() seconds.send(__more__,60) end
|
17
|
+
def hours() minutes.send(__more__,60) end
|
18
|
+
def days() hours.send(__more__,24) end
|
19
|
+
def weeks() days.send(__more__,7) end
|
20
|
+
def months() weeks.send(__more__,4) end
|
21
|
+
#def years() months.send(__more__,12) end
|
22
|
+
def years() days.send(__more__,365) end
|
23
|
+
def decades() years.send(__more__,10) end
|
24
|
+
def centuries() decades.send(__more__,10) end
|
25
|
+
instance_methods.select{|m| m !~ /__/}.each do |plural|
|
26
|
+
singular = plural.to_s.chop # Added .to_s for
|
27
|
+
alias_method singular, plural
|
28
|
+
end
|
29
|
+
end
|
30
|
+
module DiffUnits
|
31
|
+
include ::Time::Units
|
32
|
+
def __less__() "*" end
|
33
|
+
def __more__() "/" end
|
34
|
+
end
|
35
|
+
alias_method "__delta__", "-" unless respond_to? "__delta__"
|
36
|
+
def - other
|
37
|
+
ret = __delta__ other
|
38
|
+
ret.extend DiffUnits
|
39
|
+
ret
|
40
|
+
end
|
41
|
+
end
|
42
|
+
class Numeric
|
43
|
+
include ::Time::Units
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
if $0 == __FILE__
|
50
|
+
require "yaml"
|
51
|
+
require "time"
|
52
|
+
|
53
|
+
now = Time::now
|
54
|
+
|
55
|
+
a = now
|
56
|
+
y 'a' => a
|
57
|
+
|
58
|
+
b = now + 2.hours + 2.minutes
|
59
|
+
y 'b' => b
|
60
|
+
|
61
|
+
d = b - a
|
62
|
+
%w( seconds minutes hours days ).each do |unit|
|
63
|
+
y "d.#{ unit }" => d.send(unit)
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,266 @@
|
|
1
|
+
|
2
|
+
require 'uri'
|
3
|
+
require 'timeout'
|
4
|
+
require 'net/http'
|
5
|
+
|
6
|
+
module HTTPUtil
|
7
|
+
VALID_METHODS = %w{GET HEAD POST PUT DELETE}
|
8
|
+
@@timeout = 20
|
9
|
+
|
10
|
+
# Takes a string. See WEBrick::parse_header(string).
|
11
|
+
def HTTPUtil.parse_header(raw)
|
12
|
+
header = Hash.new([].freeze)
|
13
|
+
raw.each_line do |line|
|
14
|
+
case line
|
15
|
+
when /\A(.+?):\s+(.+)\z/om
|
16
|
+
name, value = $1, $2
|
17
|
+
name = name.tr('-', '_').to_sym
|
18
|
+
value.strip!
|
19
|
+
|
20
|
+
header[name] = [] unless header.has_key?(name)
|
21
|
+
header[name] << value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
header
|
25
|
+
end
|
26
|
+
|
27
|
+
# Takes a string or array. See parse_header_body for further info.
|
28
|
+
# Returns +method+, +http_version+, +uri+, +header+, +body+
|
29
|
+
def HTTPUtil.parse_http_request(data, host=:unknown, port=80)
|
30
|
+
return unless data && !data.empty?
|
31
|
+
data = data.split(/\r?\n/) unless data.kind_of? Array
|
32
|
+
data.shift while (data[0].empty? || data[0].nil?) # Remove leading empties
|
33
|
+
request_line = data.shift # i.e. GET /path HTTP/1.1
|
34
|
+
method, path, http_version = nil
|
35
|
+
|
36
|
+
# With WEBrick and other proxies, the entire URI is included in HTTP requests.
|
37
|
+
# i.e. GET http://stellaaahhhh.com/streetcar.png HTTP/1.1
|
38
|
+
# The parser is expecting just the absolute path.
|
39
|
+
if request_line =~ /^(\S+)\s+(http:\/\/.+)\s+(HTTP.+)?/mo
|
40
|
+
uri = URI.parse($2)
|
41
|
+
request_line = "#{$1} #{uri.request_uri} #{$3}"
|
42
|
+
host = uri.host
|
43
|
+
end
|
44
|
+
|
45
|
+
if request_line =~ /^(\S+)\s+(\S+)(?:\s+HTTP\/(\d+\.\d+))?/mo
|
46
|
+
method = $1
|
47
|
+
http_version = $3 # Comes before $2 b/c the split resets the numbered vars
|
48
|
+
path, query_string = $2.split('?')
|
49
|
+
|
50
|
+
# We only process the header and body data when we know we're
|
51
|
+
# starting from the beginning of a request string. We don't
|
52
|
+
# want no partials.
|
53
|
+
header, body = HTTPUtil.parse_header_body(data)
|
54
|
+
query = HTTPUtil.parse_query(method, query_string)
|
55
|
+
|
56
|
+
|
57
|
+
# TODO: Parse username/password
|
58
|
+
uri = URI::HTTP.build({
|
59
|
+
:scheme => 'http',
|
60
|
+
:host => header[:Host][0] || host.to_s,
|
61
|
+
:port => port,
|
62
|
+
:path => path,
|
63
|
+
:query => query_string
|
64
|
+
})
|
65
|
+
|
66
|
+
else
|
67
|
+
rl = request_line.sub(/\x0d?\x0a\z/o, '')
|
68
|
+
raise "Bad Request-Line `#{rl}'."
|
69
|
+
end
|
70
|
+
|
71
|
+
return method, http_version, uri, header, body
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
# Takes a string or array. See parse_header_body for further info.
|
76
|
+
# Returns +status+, +http_version+, +message+, +header+, +body+
|
77
|
+
def HTTPUtil.parse_http_response(data=[])
|
78
|
+
return unless data && !data.empty?
|
79
|
+
data = data.split(/\r?\n/) unless data.kind_of? Array
|
80
|
+
data.shift while (data[0].empty? || data[0].nil?) # Remove leading empties
|
81
|
+
status_line = data.shift # ie. HTTP/1.1 200 OK
|
82
|
+
http_version, status, message = nil
|
83
|
+
|
84
|
+
if status_line =~ /^HTTP\/(\d.+?)(\s+(\d\d\d)\s+(.+))?$/mo
|
85
|
+
http_version = $1
|
86
|
+
status = $2
|
87
|
+
message = $4
|
88
|
+
|
89
|
+
header, body, query = HTTPUtil.parse_header_body(data)
|
90
|
+
|
91
|
+
else
|
92
|
+
raise "Bad Response-Line `#{status_line}'."
|
93
|
+
end
|
94
|
+
|
95
|
+
return status, http_version, message, header, body
|
96
|
+
end
|
97
|
+
|
98
|
+
# Process everything after the first line of an HTTP request or response:
|
99
|
+
# GET / HTTP/1.1
|
100
|
+
# HTTP/1.1 200 OK
|
101
|
+
# etc...
|
102
|
+
# Used by parse_http_request and parse_http_response but can be used separately.
|
103
|
+
# Takes a string or array of strings. A string should be formatted like an HTTP
|
104
|
+
# request or response. If a body is present it should be separated by two newlines.
|
105
|
+
# An array of string should contain an empty or nil element between the header
|
106
|
+
# and body content. This will happen naturally if the raw lines were split by
|
107
|
+
# a single line terminator. (i.e. /\n/ rather than /\n\n/)
|
108
|
+
# Returns header (hash), body (string)
|
109
|
+
def HTTPUtil.parse_header_body(data=[])
|
110
|
+
header, body = {}, nil
|
111
|
+
data = data.split(/\r?\n/) unless data.kind_of? Array
|
112
|
+
data.shift while (data[0].nil? || data[0].empty?) # Remove leading empties
|
113
|
+
|
114
|
+
return header, body unless data && !data.empty?
|
115
|
+
|
116
|
+
#puts data.to_yaml
|
117
|
+
|
118
|
+
# Skip that first line if it exists
|
119
|
+
data.shift if data[0].match(/\AHTTP|GET|POST|DELETE|PUT|HEAD/mo)
|
120
|
+
|
121
|
+
header_lines = []
|
122
|
+
header_lines << data.shift while (!data[0].nil? && !data[0].empty?)
|
123
|
+
header = HTTPUtil::parse_header(header_lines.join($/))
|
124
|
+
|
125
|
+
# We omit the blank line that delimits the header from the body
|
126
|
+
body = data[1..-1].join($/) unless data.empty?
|
127
|
+
|
128
|
+
return header, body
|
129
|
+
end
|
130
|
+
|
131
|
+
def HTTPUtil.parse_query(request_method, query_string, content_type='', body='')
|
132
|
+
query = Hash.new([].freeze)
|
133
|
+
|
134
|
+
if request_method == "GET" || request_method == "HEAD"
|
135
|
+
query = HTTPUtil::parse_query_from_string(query_string)
|
136
|
+
elsif content_type =~ /^application\/x-www-form-urlencoded/
|
137
|
+
query = HTTPUtil::parse_query_from_string(body)
|
138
|
+
elsif content_type =~ /^multipart\/form-data; boundary=(.+)/
|
139
|
+
boundary = $1.tr('"', '')
|
140
|
+
query = HTTPUtil::parse_form_data(body, boundary)
|
141
|
+
else
|
142
|
+
query
|
143
|
+
end
|
144
|
+
|
145
|
+
query
|
146
|
+
end
|
147
|
+
|
148
|
+
def HTTPUtil.validate_method(meth='GET')
|
149
|
+
(VALID_METHODS.member? meth.upcase) ? meth : VALID_METHODS[0]
|
150
|
+
end
|
151
|
+
|
152
|
+
# Parses a query string by breaking it up at the '&'
|
153
|
+
# and ';' characters. You can also use this to parse
|
154
|
+
# cookies by changing the characters used in the second
|
155
|
+
# parameter (which defaults to '&;'.
|
156
|
+
# Stolen from Mongrel
|
157
|
+
def HTTPUtil.parse_query_from_string(qs, d = '&;')
|
158
|
+
params = {}
|
159
|
+
(qs||'').split(/[#{d}] */n).inject(params) { |h,p|
|
160
|
+
k, v=unescape(p).split('=',2)
|
161
|
+
next unless k
|
162
|
+
k = k.tr('-', '_').to_sym
|
163
|
+
if cur = params[k]
|
164
|
+
if cur.class == Array
|
165
|
+
params[k] << v
|
166
|
+
else
|
167
|
+
params[k] = [cur, v]
|
168
|
+
end
|
169
|
+
else
|
170
|
+
params[k] = v
|
171
|
+
end
|
172
|
+
}
|
173
|
+
|
174
|
+
return params
|
175
|
+
end
|
176
|
+
|
177
|
+
|
178
|
+
|
179
|
+
# Based on WEBrick::HTTPutils::parse_form_data
|
180
|
+
def HTTPUtil.parse_form_data(io, boundary)
|
181
|
+
boundary_regexp = /\A--#{boundary}(--)?#{$/}\z/
|
182
|
+
form_data = Hash.new
|
183
|
+
return form_data unless io
|
184
|
+
data = nil
|
185
|
+
io.each_line{|line|
|
186
|
+
if boundary_regexp =~ line
|
187
|
+
if data
|
188
|
+
data.chop!
|
189
|
+
key = data.name.tr('-', '_').to_sym
|
190
|
+
if form_data.has_key?(key)
|
191
|
+
form_data[key].append_data(data)
|
192
|
+
else
|
193
|
+
form_data[key] = data
|
194
|
+
end
|
195
|
+
end
|
196
|
+
data = FormData.new
|
197
|
+
next
|
198
|
+
else
|
199
|
+
if data
|
200
|
+
data << line
|
201
|
+
end
|
202
|
+
end
|
203
|
+
}
|
204
|
+
return form_data
|
205
|
+
end
|
206
|
+
|
207
|
+
|
208
|
+
# Extend the basic query string parser provided by the cgi module.
|
209
|
+
# converts single valued params (the most common case) to
|
210
|
+
# objects instead of arrays
|
211
|
+
#
|
212
|
+
# Input:
|
213
|
+
# the query string
|
214
|
+
#
|
215
|
+
# Output:
|
216
|
+
# hash of parameters, contains arrays for multivalued parameters
|
217
|
+
# (multiselect, checkboxes , etc)
|
218
|
+
# If no query string is provided (nil or "") returns an empty hash.
|
219
|
+
def HTTPUtil.query_to_hash(query_string)
|
220
|
+
return {} unless query_string
|
221
|
+
|
222
|
+
query_parameters = HTTPUtil.parse_query(query_string)
|
223
|
+
|
224
|
+
query_parameters.each { |key, val|
|
225
|
+
# replace the array with an object
|
226
|
+
query_parameters[key] = val[0] if 1 == val.length
|
227
|
+
}
|
228
|
+
|
229
|
+
# set default value to nil! cgi sets this to []
|
230
|
+
query_parameters.default = nil
|
231
|
+
|
232
|
+
return query_parameters
|
233
|
+
end
|
234
|
+
|
235
|
+
def HTTPUtil.hash_to_query(parameters)
|
236
|
+
return '' unless parameters
|
237
|
+
pairs = []
|
238
|
+
parameters.each do |param, value|
|
239
|
+
pairs << "#{param}=#{URI.escape(value.to_s)}"
|
240
|
+
end
|
241
|
+
return pairs.join('&')
|
242
|
+
#return pairs.join(";")
|
243
|
+
end
|
244
|
+
|
245
|
+
|
246
|
+
|
247
|
+
# Performs URI escaping so that you can construct proper
|
248
|
+
# query strings faster. Use this rather than the cgi.rb
|
249
|
+
# version since it's faster. (Stolen from Mongrel/Camping).
|
250
|
+
def HTTPUtil.escape(s)
|
251
|
+
s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
|
252
|
+
'%'+$1.unpack('H2'*$1.size).join('%').upcase
|
253
|
+
}.tr(' ', '+')
|
254
|
+
end
|
255
|
+
|
256
|
+
|
257
|
+
# Unescapes a URI escaped string. (Stolen from Mongrel/Camping).
|
258
|
+
def HTTPUtil.unescape(s)
|
259
|
+
s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
|
260
|
+
[$1.delete('%')].pack('H*')
|
261
|
+
}
|
262
|
+
end
|
263
|
+
|
264
|
+
|
265
|
+
end
|
266
|
+
|
data/stella.gemspec
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
@spec = Gem::Specification.new do |s|
|
2
|
+
s.name = "stella"
|
3
|
+
s.rubyforge_project = "stella"
|
4
|
+
s.version = "0.6.0"
|
5
|
+
s.summary = "Your friend in performance testing"
|
6
|
+
s.description = s.summary
|
7
|
+
s.author = "Delano Mandelbaum"
|
8
|
+
s.email = "delano@solutious.com"
|
9
|
+
s.homepage = "http://github.com/solutious/stella"
|
10
|
+
|
11
|
+
# = DEPENDENCIES =
|
12
|
+
# Add all gem dependencies
|
13
|
+
s.add_dependency 'httpclient'
|
14
|
+
|
15
|
+
|
16
|
+
# = MANIFEST =
|
17
|
+
# The complete list of files to be included in the release. When GitHub packages your gem,
|
18
|
+
# it doesn't allow you to run any command that accesses the filesystem. You will get an
|
19
|
+
# error. You can ask your VCS for the list of versioned files:
|
20
|
+
# git ls-files
|
21
|
+
# svn list -R
|
22
|
+
s.files = %w(
|
23
|
+
CHANGES.txt
|
24
|
+
LICENSE.txt
|
25
|
+
README.rdoc
|
26
|
+
Rakefile
|
27
|
+
bin/example_test.rb
|
28
|
+
bin/example_webapp.rb
|
29
|
+
lib/logger.rb
|
30
|
+
lib/stella.rb
|
31
|
+
lib/stella/clients.rb
|
32
|
+
lib/stella/command/base.rb
|
33
|
+
lib/stella/command/form.rb
|
34
|
+
lib/stella/command/get.rb
|
35
|
+
lib/stella/common.rb
|
36
|
+
lib/stella/crypto.rb
|
37
|
+
lib/stella/data/domain.rb
|
38
|
+
lib/stella/data/http.rb
|
39
|
+
lib/stella/environment.rb
|
40
|
+
lib/stella/functest.rb
|
41
|
+
lib/stella/loadtest.rb
|
42
|
+
lib/stella/stats.rb
|
43
|
+
lib/stella/testplan.rb
|
44
|
+
lib/stella/testrunner.rb
|
45
|
+
lib/storable.rb
|
46
|
+
lib/threadify.rb
|
47
|
+
lib/timeunits.rb
|
48
|
+
lib/util/httputil.rb
|
49
|
+
stella.gemspec
|
50
|
+
tryouts/drb/drb_test.rb
|
51
|
+
tryouts/drb/open4.rb
|
52
|
+
tryouts/drb/slave.rb
|
53
|
+
tryouts/oo_tryout.rb
|
54
|
+
)
|
55
|
+
|
56
|
+
# = EXECUTABLES =
|
57
|
+
# The list of executables in your project (if any). Don't include the path,
|
58
|
+
# just the base filename.
|
59
|
+
s.executables = %w[]
|
60
|
+
|
61
|
+
s.extra_rdoc_files = %w[README.rdoc LICENSE.txt]
|
62
|
+
s.rdoc_options = ["--line-numbers", "--title", s.summary, "--main", "README.rdoc"]
|
63
|
+
|
64
|
+
s.has_rdoc = true
|
65
|
+
s.require_paths = %w[lib]
|
66
|
+
s.rubygems_version = '1.1.1'
|
67
|
+
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
# simple_client.rb
|
3
|
+
# A simple DRb client
|
4
|
+
|
5
|
+
require 'drb'
|
6
|
+
|
7
|
+
DRb.start_service
|
8
|
+
|
9
|
+
# attach to the DRb server via a URI given on the command line
|
10
|
+
remote_array = DRbObject.new [], ARGV.shift
|
11
|
+
|
12
|
+
puts remote_array.size
|
13
|
+
|
14
|
+
remote_array << 1
|
15
|
+
|
16
|
+
puts remote_array.size
|
17
|
+
|
18
|
+
__END__
|
19
|
+
# Fastthread patches: http://blog.phusion.nl/2009/02/02/getting-ready-for-ruby-191/
|
20
|
+
# <link rel="canonical" href="http://www.seomoz.org/blog">
|
21
|
+
|
22
|
+
require 'rubygems'
|
23
|
+
require 'eventmachine'
|
24
|
+
|
25
|
+
module Echo
|
26
|
+
def receive_data data
|
27
|
+
send_data data
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
EM.run {
|
32
|
+
EM.start_server "0.0.0.0", 10000, Echo
|
33
|
+
}
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
__END__
|
38
|
+
class DSL
|
39
|
+
# Get a metaclass for this class
|
40
|
+
def self.metaclass; class << self; self; end; end
|
41
|
+
|
42
|
+
metaclass.instance_eval do
|
43
|
+
define_method( :'POST /api/suggestions.json' ) do |val|
|
44
|
+
puts val
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
class Runner
|
52
|
+
attr_accessor :poop
|
53
|
+
def hi
|
54
|
+
@poop = :rock
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
c=Runner.new
|
59
|
+
|
60
|
+
c.instance_eval do
|
61
|
+
hi
|
62
|
+
puts @poop
|
63
|
+
end
|
64
|
+
|
65
|
+
#DSL.send(:'POST /api/suggestions.json', :hi)
|