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.
Files changed (83) hide show
  1. data/CHANGES.txt +39 -2
  2. data/LICENSE.txt +19 -0
  3. data/README.rdoc +85 -0
  4. data/Rakefile +54 -59
  5. data/bin/example_test.rb +82 -0
  6. data/bin/example_webapp.rb +63 -0
  7. data/lib/{stella/logger.rb → logger.rb} +6 -11
  8. data/lib/stella.rb +76 -58
  9. data/lib/stella/clients.rb +161 -0
  10. data/lib/stella/command/base.rb +4 -24
  11. data/lib/stella/command/form.rb +36 -0
  12. data/lib/stella/command/get.rb +44 -0
  13. data/lib/stella/common.rb +53 -0
  14. data/lib/stella/crypto.rb +88 -0
  15. data/lib/stella/data/domain.rb +2 -2
  16. data/lib/stella/data/http.rb +164 -36
  17. data/lib/stella/environment.rb +66 -0
  18. data/lib/stella/functest.rb +105 -0
  19. data/lib/stella/loadtest.rb +186 -0
  20. data/lib/{utils → stella}/stats.rb +16 -20
  21. data/lib/stella/testplan.rb +237 -0
  22. data/lib/stella/testrunner.rb +64 -0
  23. data/lib/storable.rb +280 -0
  24. data/lib/threadify.rb +171 -0
  25. data/lib/timeunits.rb +65 -0
  26. data/lib/util/httputil.rb +266 -0
  27. data/stella.gemspec +69 -0
  28. data/tryouts/drb/drb_test.rb +65 -0
  29. data/tryouts/drb/open4.rb +19 -0
  30. data/tryouts/drb/slave.rb +27 -0
  31. data/tryouts/oo_tryout.rb +30 -0
  32. metadata +39 -107
  33. data/README.textile +0 -162
  34. data/bin/stella +0 -12
  35. data/bin/stella.bat +0 -12
  36. data/lib/daemonize.rb +0 -56
  37. data/lib/pcaplet.rb +0 -180
  38. data/lib/stella/adapter/ab.rb +0 -337
  39. data/lib/stella/adapter/base.rb +0 -106
  40. data/lib/stella/adapter/httperf.rb +0 -305
  41. data/lib/stella/adapter/pcap_watcher.rb +0 -221
  42. data/lib/stella/adapter/proxy_watcher.rb +0 -76
  43. data/lib/stella/adapter/siege.rb +0 -341
  44. data/lib/stella/cli.rb +0 -258
  45. data/lib/stella/cli/agents.rb +0 -73
  46. data/lib/stella/cli/base.rb +0 -55
  47. data/lib/stella/cli/language.rb +0 -18
  48. data/lib/stella/cli/localtest.rb +0 -78
  49. data/lib/stella/cli/sysinfo.rb +0 -16
  50. data/lib/stella/cli/watch.rb +0 -278
  51. data/lib/stella/command/localtest.rb +0 -358
  52. data/lib/stella/response.rb +0 -85
  53. data/lib/stella/storable.rb +0 -201
  54. data/lib/stella/support.rb +0 -276
  55. data/lib/stella/sysinfo.rb +0 -257
  56. data/lib/stella/test/definition.rb +0 -79
  57. data/lib/stella/test/run/summary.rb +0 -70
  58. data/lib/stella/test/stats.rb +0 -114
  59. data/lib/stella/text.rb +0 -64
  60. data/lib/stella/text/resource.rb +0 -38
  61. data/lib/utils/crypto-key.rb +0 -84
  62. data/lib/utils/domainutil.rb +0 -47
  63. data/lib/utils/escape.rb +0 -302
  64. data/lib/utils/fileutil.rb +0 -78
  65. data/lib/utils/httputil.rb +0 -266
  66. data/lib/utils/mathutil.rb +0 -15
  67. data/lib/utils/textgraph.rb +0 -267
  68. data/lib/utils/timerutil.rb +0 -58
  69. data/lib/win32/Console.rb +0 -970
  70. data/lib/win32/Console/ANSI.rb +0 -305
  71. data/support/kvm.h +0 -91
  72. data/support/ruby-pcap-takuma-notes.txt +0 -19
  73. data/support/ruby-pcap-takuma-patch.txt +0 -30
  74. data/support/text/en.yaml +0 -80
  75. data/support/text/nl.yaml +0 -7
  76. data/support/useragents.txt +0 -75
  77. data/tests/01-util_test.rb +0 -0
  78. data/tests/02-stella-util_test.rb +0 -42
  79. data/tests/10-stella_test.rb +0 -104
  80. data/tests/11-stella-storable_test.rb +0 -68
  81. data/tests/60-stella-command_test.rb +0 -248
  82. data/tests/80-stella-cli_test.rb +0 -45
  83. data/tests/spec-helper.rb +0 -31
@@ -1,358 +0,0 @@
1
-
2
-
3
- module Stella
4
- class LocalTest < Stella::Command::Base
5
-
6
- # A container for the test parameters
7
- attr_accessor :testdef
8
- # The load tool adapter
9
- attr_accessor :adapter
10
-
11
- attr_accessor :test_path
12
-
13
- attr_accessor :quiet
14
- attr_accessor :guid
15
- attr_accessor :verbose
16
- attr_accessor :format
17
-
18
- # list of all filesystem paths for each run in a single test
19
- attr_reader :test_runpaths
20
- # same as above, expect for all tests
21
- attr_reader :all_runpaths
22
- # list of all hostname:port in the test
23
- attr_reader :hosts
24
- # list of all /paths in the test
25
- attr_reader :paths
26
-
27
- attr_accessor :working_directory
28
- attr_reader :available_agents
29
- attr_reader :test_stats
30
- attr_reader :rampup_test_stats
31
-
32
- def initialize(testdef=nil, adapter=nil)
33
- @testdef = testdef if testdef
34
- @adapter = adapter if adapter
35
-
36
- # Disabled until we resolve JRuby on OSX issue (won't load openssl)
37
- #@guid = Crypto.sign(rand.to_s, rand.to_s)
38
-
39
- @test_runpaths = []
40
- @all_runpaths = []
41
- @hosts = []
42
- @paths = []
43
- @format = 'yaml'
44
- @agents = []
45
- @verbose = 0
46
-
47
- ua_path = File.join(STELLA_HOME, 'support', 'useragents.txt')
48
- Stella::LOGGER.debug("LOADING #{ua_path}")
49
-
50
- @available_agents = Stella::Util.process_useragents(ua_path)
51
- end
52
-
53
-
54
-
55
- def run
56
-
57
-
58
- raise UnavailableAdapter.new(@adapter.name) unless @adapter.available?
59
-
60
- # If the adapter isn't being called for a loadtest, we don't have anything to do.
61
- unless @adapter.loadtest?
62
- system(@adapter.command)
63
- return
64
- end
65
-
66
- @agents = Stella::Util.find_agents(@available_agents, @testdef.agents)
67
-
68
- @test_path = _generate_test_path
69
-
70
- prepare_test(@test_path)
71
-
72
- threshold = (@testdef.rampup) ? @testdef.rampup.ceiling : @adapter.vusers
73
-
74
- runnum = "00"
75
- while(@adapter.vusers <= threshold) do
76
-
77
- # Make sure the load factor is set to 1. If there was a warmup,
78
- # then this value will be less than 1.
79
- testrun = maker_of_testruns(1)
80
-
81
- # We empty test_runpaths so we can keep track of the runpaths for each
82
- # level of virtual users. This is useful during a rampup test so we can
83
- # collect the stats for the repetitions at each virtual user level.
84
- @test_runpaths = []
85
-
86
- # Execute each test run in turn. All of the steps are the same for each
87
- # run. This is important because the purpose of the test runs is to
88
- # create a statistical certainty of performance.
89
- @testdef.repetitions.times do
90
-
91
- # This value is used to create the run directory and show which run we're on
92
- runnum.succ!
93
-
94
- # Generate the filesystem path to store the run data.
95
- runpath = File.join(test_path, "run#{runnum}")
96
-
97
- # Keep track of every run path. We use this in postpare to
98
- # do some stuff after all the tests have run
99
- @all_runpaths << runpath
100
-
101
- # test_runpaths will be identical to all_runpaths except when there is a
102
- # rampup. In that case, test_runpaths will contain the runpaths for all
103
- # repetitions of each level of virtual users.
104
- @test_runpaths << runpath
105
-
106
- testrun.call("Run #{runnum}", runpath)
107
-
108
- # Only sleep if requested and there is more than 1 test run
109
- if @testdef.sleep && @testdef.sleep.to_f > 0 && @testdef.repetitions > 1
110
- run_sleeper(@testdef.sleep)
111
- end
112
- Stella::LOGGER.info('') unless @quiet # We want the newline
113
- end
114
-
115
- # Rampup tests produce multiple summary files which include the batch
116
- # number. Regular runs have just one file we set here as the default.
117
- summary_name = "STATS"
118
-
119
- # It's possible for the interval to not divide evenly into the ceiling
120
- # If we have room between the current number of virtual users and the
121
- # ceiling, we'll add the difference for the final test run.
122
- if (@testdef.rampup)
123
- final_total = @adapter.vusers + @testdef.rampup.interval
124
- if final_total > threshold && (threshold - @adapter.vusers) > 0
125
- @adapter.vusers += (threshold - @adapter.vusers)
126
- else
127
- @adapter.vusers += @testdef.rampup.interval
128
- end
129
- padded_users = @adapter.vusers.to_s.rjust(4, '0')
130
- summary_name << "-#{padded_users}"
131
- end
132
-
133
- # Read the run summaries for this batch and produce totals, averages,
134
- # and standard deviations.
135
- @test_stats = process_test_stats(@test_runpaths)
136
- print_summary(test_stats) if (@testdef.repetitions > 1)
137
- save_summary(File.join(test_path, "#{summary_name}.#{@format}"), @test_stats)
138
-
139
-
140
- # Non-rampup tests only need to run through the loop once.
141
- break if (!@testdef.rampup && @adapter.vusers == threshold)
142
-
143
- end
144
-
145
- if @testdef.rampup
146
- # Notice the difference between these test stats and the ones above?
147
- # These stats are based on the entire rampup test, across all levels
148
- # of virtual users.
149
- @rampup_test_stats = process_test_stats(@all_runpaths)
150
- save_summary(File.join(test_path, "STATS.#{@format}"), @rampup_test_stats)
151
- print_summary(test_stats) if (@testdef.repetitions > 1)
152
- end
153
- rescue Interrupt
154
- exit
155
- rescue AdapterError => ex
156
- Stella::LOGGER.error(ex.message)
157
- end
158
-
159
- def test_path_symlink
160
- return unless @working_directory
161
- File.join(@working_directory, 'latest')
162
- end
163
-
164
-
165
- private
166
-
167
- # prepare_test
168
- #
169
- # Execute the group of testruns associated to this test. This includes
170
- # zero or one warmup and one or more testruns.
171
- #
172
- # INPUT:
173
- # +test_path+ filesystem path to store all test data
174
- #
175
- def prepare_test(test_path)
176
-
177
- Stella::LOGGER.info("Writing test data to: #{test_path}\n\n") unless @quiet
178
-
179
- # Make sure the test storage directory is created along with the
180
- # latest symlink
181
- FileUtil.create_dir(test_path)
182
- if Stella.sysinfo.os == :unix
183
- File.unlink(test_path_symlink) if File.exists?(test_path_symlink)
184
- File.symlink(File.expand_path(test_path), test_path_symlink)
185
- end
186
-
187
- # Write the test ID to the storage directory
188
- # NOTE: Disabled until we resolve the issue with JRuby on OSX (won't load openssl)
189
- #FileUtil.write_file(test_path + "/ID.txt", @guid, true)
190
-
191
- # And the test run message
192
- FileUtil.write_file(test_path + "/MESSAGE.txt", @testdef.message, true) if @testdef.message
193
-
194
- @adapter.user_agent = @agents unless @agents.empty?
195
-
196
- # The warmup is identical to a testrun except for two things:
197
- # - we don't make note of the runpath
198
- # - there is a one second sleep after the run.
199
- # Everything else is identical.
200
- if @testdef.warmup
201
-
202
- testrun = maker_of_testruns(@testdef.warmup)
203
-
204
- # Generate the filesystem path to store the run data.
205
- # NOTE: We don't keep the warmup path in @test_runpaths because we
206
- # include it in the final calculation for the test.
207
- runpath = File.join(test_path, "warmup")
208
-
209
- # Run the warmpup round
210
- testrun.call("Warmup", runpath)
211
-
212
- run_sleeper(@testdef.sleep || 1)
213
-
214
- Stella::LOGGER.info('', '') unless @quiet # We just need the newline
215
-
216
- end
217
-
218
- print_title unless @quiet
219
-
220
- end
221
-
222
- # maker_of_testruns
223
- #
224
- # Generator of test runs. Everything that happens during a test
225
- # run is defined here. We use a Proc so we can toss the functionality
226
- # around like something that's dirty and loves a good tossing.
227
- # It's important that all output be on a single line without a
228
- # line terminator. Otherwise great descruction could occur.
229
- def maker_of_testruns(factor)
230
- testrun = Proc.new do |name,runpath|
231
- # Make sure the test run storage directory is created
232
- FileUtil.create_dir(runpath)
233
-
234
- @adapter.load_factor = factor
235
- @adapter.working_directory = runpath
236
- @adapter.before
237
-
238
- Stella::LOGGER.info_printf("%8s: %10d@%-6s ", name, @adapter.requests, @adapter.vuser_rate) unless @quiet
239
-
240
- # Here we record the command arguments. This needs to be last because we modify
241
- # some of the arguments above.
242
- FileUtil.write_file(runpath + "/COMMAND.txt", @adapter.command, true)
243
-
244
- # Execute the command, send STDOUT and STDERR to separate files.
245
- command = "#{@adapter.command} 1> \"#{@adapter.stdout_path}\" 2> \"#{@adapter.stderr_path}\""
246
- Stella::LOGGER.info(" COMMAND: #{command}") if @verbose >= 2
247
-
248
- begin
249
- # Call the load tool
250
- # $? contains the error status
251
- succeeded = system(command)
252
-
253
- # TODO: Catch interrupts for system calls. Currently it will simply and and continue with the next command
254
- # i.e. these don't work:
255
- rescue Interrupt
256
- exit
257
- rescue SystemExit
258
- exit
259
- end
260
-
261
- unless succeeded
262
- Stella::LOGGER.info('', '') # We used print so we need a new line for the error message.
263
- raise AdapterError.new(@adapter.name, @adapter.error)
264
- end
265
-
266
- @adapter.after
267
-
268
- stats = @adapter.summary
269
-
270
- save_summary(@adapter.summary_path(@format), stats)
271
-
272
-
273
- if !@quiet && stats && stats.available?
274
- Stella::LOGGER.info_print(sprintf("%3.0f%% %9.2f/s %8.3fs ", stats.availability || 0, stats.transaction_rate || 0, stats.response_time || 0))
275
- Stella::LOGGER.info_print(sprintf("%8.3fMB/s %8.3fMB %8.3fs ", stats.throughput || 0, stats.data_transferred || 0, stats.elapsed_time || 0))
276
- # NOTE: We don't print a line terminator here
277
- end
278
- end
279
- end
280
-
281
-
282
- # print_summary
283
- #
284
- # Called during the test after every batch of test runs. For a rampup test
285
- # it's also called at the end of all the runs.
286
- #
287
- # INPUT:
288
- # stats:: Any object that extends Stella::Test::Base object
289
- def print_summary(stats)
290
- return if stats.nil?
291
- Stella::LOGGER.info(' ' << "-"*67) unless @quiet
292
-
293
- Stella::LOGGER.info_printf("%8s: %10d@%-6d %3.0f%% %9.2f/s ", "Total", stats.transactions_total || 0, stats.vusers_avg || 0, stats.availability || 0, stats.transaction_rate_avg || 0)
294
- Stella::LOGGER.info_printf("%8.3fs %8.3fMB/s %8.3fMB %8.3fs", stats.response_time_avg || 0, stats.throughput_avg || 0, stats.data_transferred_total || 0, stats.elapsed_time_total || 0)
295
- Stella::LOGGER.info('') # New line
296
- Stella::LOGGER.info_printf("%8s: %22s %9.2f/s %8.3fs %8.3fMB/s %10s %8.3fs", "Std Dev", '', stats.transaction_rate_sdev || 0, stats.response_time_sdev || 0, stats.throughput_sdev || 0, '', stats.elapsed_time_sdev || 0)
297
- Stella::LOGGER.info('') # New line
298
- Stella::LOGGER.info('') unless @quiet # Extra new line
299
- end
300
-
301
- # print_title
302
- #
303
- # Prints the column headers for the test run output. Field widths match those
304
- # in print_summary and test_maker
305
- def print_title
306
- Stella::LOGGER.info(' ' << "-"*67) unless @quiet
307
-
308
- Stella::LOGGER.info_printf("%8s %10s@%-5s %5s %11s", "", 'REQ', 'VU/s', 'AVAIL', 'REQ/s')
309
- Stella::LOGGER.info_printf("%10s %12s %10s %9s", 'RTIME', 'DATA/s', 'DATA', 'TIME')
310
- Stella::LOGGER.info('') # New line
311
- Stella::LOGGER.info('') unless @quiet # Extra new line
312
- end
313
-
314
- # save_summary
315
- #
316
- # Write a summary object to disk
317
- #
318
- # INPUT:
319
- # filepath:: the complete path for the file (string)
320
- # stats:: Any object that extends Stella::Test::Base object
321
- def save_summary(filepath, stats)
322
- return unless stats
323
- stats.format = @format
324
- stats.to_file(filepath)
325
- end
326
-
327
- # Load SUMMARY file for each run and create a summary with
328
- # totals, averages, and standard deviations.
329
- def process_test_stats(paths)
330
- return unless paths && !paths.empty?
331
- test_stats = Stella::Test::Stats.new(@message)
332
-
333
- paths.each do |path|
334
- next unless File.exists?("#{path}/SUMMARY.#{@format}")
335
- test_run = Stella::Test::Run::Summary.from_file("#{path}/SUMMARY.#{@format}")
336
- test_stats.add_run(test_run)
337
- end
338
-
339
- test_stats
340
- end
341
-
342
- # This is the path where all test data will be stored
343
- # The default is testruns/2008-12-31/test-001
344
- def _generate_test_path
345
- now = DateTime.now
346
- time = Time.now
347
- daystr = "#{now.year}-#{now.mon.to_s.rjust(2,'0')}-#{now.mday.to_s.rjust(2,'0')}"
348
- testpath = File.join(@working_directory, 'testruns', daystr, 'test-')
349
- testnum = 1.to_s.rjust(3,'0')
350
- testnum.succ! while(File.directory? "#{testpath}#{testnum}")
351
- "#{testpath}#{testnum}"
352
- end
353
-
354
-
355
-
356
- end
357
- end
358
-
@@ -1,85 +0,0 @@
1
-
2
-
3
-
4
- module Stella
5
-
6
- # An object for HTTP response content
7
- #
8
- class Response < Storable
9
-
10
- field :errors => Array
11
- field :content => Hash
12
- field :messages => Array
13
- field :success => TrueClass
14
-
15
- def initialize
16
- @success = false
17
- @errors = []
18
- @messages = []
19
- @content = {}
20
- end
21
-
22
- def success?
23
- @success
24
- end
25
-
26
- def add(key, value)
27
- @content[key] = value
28
- end
29
-
30
- def get(key)
31
- @content[key] if @content.has_key? key
32
- end
33
-
34
- def message(msg)
35
- @messages.push(msg)
36
- end
37
-
38
- def error(msg)
39
- @errors.push(msg)
40
- end
41
-
42
- def output(format='yaml')
43
- format = 'yaml' unless self.respond_to? "output_#{format}"
44
- #STDERR.puts "OUTPUT: #{format}"
45
- self.send("output_#{format}")
46
- end
47
-
48
- def to_hash
49
- h = {}
50
- h[:version] = API_VERSION
51
- h[:errors] = @errors unless @errors.empty?
52
- h[:messages] = @messages unless @messages.empty?
53
- h[:content] = @content || {}
54
- h[:success] = @success || false
55
- h
56
- end
57
-
58
- def output_zip
59
- output = @content
60
- end
61
-
62
- def output_yaml
63
- to_hash.to_yaml
64
- end
65
-
66
- # http://evang.eli.st/blog/2007/2/22/my-rails-gotcha-custom-to_xml-in-a-hash-or-array
67
- # http://api.rubyonrails.org/classes/ActiveRecord/XmlSerialization.html#M000910
68
- def output_xml
69
- output = "<StellaResponse success=\":[\">\n"
70
- output << "<todo>implement XML</todo>\n"
71
- output << "</StellaResponse>\n"
72
- end
73
-
74
- def output_json
75
- to_hash.to_json
76
- end
77
-
78
- def output_html
79
- "hello!"
80
- end
81
-
82
- end
83
-
84
- end
85
-
@@ -1,201 +0,0 @@
1
-
2
- # TODO: Handle nested hashes and arrays.
3
-
4
- require 'yaml'
5
- require 'utils/fileutil'
6
-
7
- module Stella
8
- class Storable
9
- NICE_TIME_FORMAT = "%Y-%m-%d@%H:%M:%S".freeze unless defined? NICE_TIME_FORMAT
10
- SUPPORTED_FORMATS = %w{tsv csv yaml json}.freeze unless defined? SUPPORTED_FORMATS
11
-
12
- attr_reader :format
13
-
14
- def format=(v)
15
- raise "Unsupported format: #{v}" unless SUPPORTED_FORMATS.member?(v)
16
- @format = v
17
- end
18
-
19
- def init
20
- self.class.send(:class_variable_set, :@@field_names, []) unless class_variable_defined?(:@@field_names)
21
- self.class.send(:class_variable_set, :@@field_types, []) unless class_variable_defined?(:@@field_types)
22
- end
23
-
24
- def self.field(args={})
25
- args.each_pair do |m,t|
26
-
27
- [[:@@field_names, m], [:@@field_types, t]].each do |tuple|
28
- class_variable_set(tuple[0], []) unless class_variable_defined?(tuple[0])
29
- class_variable_set(tuple[0], class_variable_get(tuple[0]) << tuple[1])
30
- end
31
-
32
- next if method_defined?(m)
33
-
34
- # NOTE: I need a way to put these in the caller's namespace... Here's they're shared by all
35
- # the subclasses which is not helpful. It will likely involve Kernel#caller and binding.
36
- # Maybe class_eval, wraped around def field.
37
-
38
-
39
- define_method(m) do instance_variable_get("@#{m}") end
40
-
41
- define_method("#{m}=") do |val|
42
- instance_variable_set("@#{m}",val)
43
- end
44
- end
45
- end
46
-
47
- def self.field_names
48
- class_variable_get(:@@field_names)
49
- end
50
- def self.field_types
51
- class_variable_get(:@@field_types)
52
- end
53
-
54
- def field_names
55
- self.class.send(:class_variable_get, :@@field_names)
56
- end
57
-
58
- def field_types
59
- self.class.send(:class_variable_get, :@@field_types)
60
- end
61
-
62
- def format=(v)
63
- raise "Unsupported format: #{v}" unless SUPPORTED_FORMATS.member?(v)
64
- @format = v
65
- end
66
-
67
-
68
- def dump(format=nil, with_titles=true)
69
- format ||= @format
70
- raise "Format not defined (#{format})" unless SUPPORTED_FORMATS.member?(format)
71
- send("to_#{format}", with_titles)
72
- end
73
-
74
- def self.from_file(file_path=nil, format=nil)
75
- raise "Cannot read file (#{file_path})" unless File.exists?(file_path)
76
- format = format || File.extname(file_path).tr('.', '')
77
- me = send("from_#{format}", FileUtil.read_file_to_array(file_path))
78
- me.format = format
79
- me
80
- end
81
- def to_file(file_path=nil, with_titles=true)
82
- raise "Cannot store to nil path" if file_path.nil?
83
- format = File.extname(file_path).tr('.', '')
84
- format ||= @format
85
- FileUtil.write_file(file_path, dump(format, with_titles))
86
- end
87
-
88
-
89
- def self.from_hash(from={})
90
- me = self.new
91
-
92
- return me if !from || from.empty?
93
-
94
- fnames = field_names
95
- fnames.each_with_index do |key,index|
96
-
97
- value = from[key]
98
-
99
- # TODO: Correct this horrible implementation (sorry, me. It's just one of those days.)
100
-
101
- if field_types[index] == Time
102
- value = Time.parse(from[key].to_s)
103
- elsif field_types[index] == DateTime
104
- value = DateTime.parse(from[key].to_s)
105
- elsif field_types[index] == TrueClass
106
- value = (from[key].to_s == "true")
107
- elsif field_types[index] == Float
108
- value = from[key].to_f
109
- elsif field_types[index] == Integer
110
- value = from[key].to_i
111
- end
112
-
113
- me.send("#{key}=", value) if self.method_defined?("#{key}=")
114
- end
115
- me
116
- end
117
- def to_hash(with_titles=true)
118
- tmp = {}
119
- field_names.each do |fname|
120
- tmp[fname] = self.send(fname)
121
- end
122
- tmp
123
- end
124
-
125
-
126
- def self.from_yaml(from=[])
127
- # from is an array of strings
128
- from_str = from.join('')
129
- hash = YAML::load(from_str)
130
- hash = from_hash(hash) if hash.is_a? Hash
131
- hash
132
- end
133
- def to_yaml(with_titles=true)
134
- to_hash.to_yaml
135
- end
136
-
137
-
138
- def self.from_json(from=[])
139
- require 'json'
140
- # from is an array of strings
141
- from_str = from.join('')
142
- tmp = JSON::load(from_str)
143
- hash_sym = tmp.keys.inject({}) do |hash, key|
144
- hash[key.to_sym] = tmp[key]
145
- hash
146
- end
147
- hash_sym = from_hash(hash_sym) if hash_sym.is_a? Hash
148
- hash_sym
149
- end
150
- def to_json(with_titles=true)
151
- require 'json'
152
- to_hash.to_json
153
- end
154
-
155
- def to_delimited(with_titles=false, delim=',')
156
- values = []
157
- field_names.each do |fname|
158
- values << self.send(fname.to_s) # TODO: escape values
159
- end
160
- output = values.join(delim)
161
- output = field_names.join(delim) << $/ << output if with_titles
162
- output
163
- end
164
- def to_tsv(with_titles=false)
165
- to_delimited(with_titles, "\t")
166
- end
167
- def to_csv(with_titles=false)
168
- to_delimited(with_titles, ',')
169
- end
170
- def self.from_tsv(from=[])
171
- self.from_delimited(from, "\t")
172
- end
173
- def self.from_csv(from=[])
174
- self.from_delimited(from, ',')
175
- end
176
-
177
- def self.from_delimited(from=[],delim=',')
178
- return if from.empty?
179
- # We grab an instance of the class so we can
180
- hash = {}
181
-
182
- fnames = values = []
183
- if (from.size > 1 && !from[1].empty?)
184
- fnames = from[0].chomp.split(delim)
185
- values = from[1].chomp.split(delim)
186
- else
187
- fnames = self.field_names
188
- values = from[0].chomp.split(delim)
189
- end
190
-
191
- fnames.each_with_index do |key,index|
192
- next unless values[index]
193
- hash[key.to_sym] = values[index]
194
- end
195
- hash = from_hash(hash) if hash.is_a? Hash
196
- hash
197
- end
198
-
199
-
200
- end
201
- end