solutious-stella 0.5.5 → 0.6.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.
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