Class: VORuby::ActiveVOTable::Base
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- VORuby::ActiveVOTable::Base
- Defined in:
- lib/voruby/active_votable/active_votable.rb
Overview
Have you ever wished you could treat your VOTable as a database? Well, now you can. ActiveVOTable is a package for reading votables into a relational database. It uses the SAX parser in LibXML so it can handle votables of arbitrary size, and it wraps the resulting database tables in ActiveRecord so that you can easily execute queries against it. It can even handle votables with multiple tables in multiple resources.
ActiveVOTable::Base.logger = Logger.new(STDOUT) # set the logger to output to STDOUT, exactly as in ActiveRecord
ActiveVOTable::Base.establish_connection(:adapter => 'sqlite3', :database => 'votables.sqlite3') # connect to the database
# read in the file results.vot into a SQLite database
vot = ActiveVOTable::Base.from(:xml, File.new('results.vot'), 'results')
table = vot.first # a votable can have more than one TABLE element, but we're interested in the first one.
# every table has a data section and schema section, both of which are accessible
data = table.data
schema = table.schema
# both data and schemata are ultimately simply subclasses of ActiveRecord::Base and have all the same methods
puts data.find(:first).inspect
# => <VORuby::ActiveVOTable::Base::Result11::ResultData11 id: 1, resource_num: 1, table_num: 1, ra: 10.68, dec: 41.27, name: "N 224", rvel: -297, e_rvel: 5, r: 0.7>
puts schema.find(:first).inspect
# => #<VORuby::ActiveVOTable::Base::Result11::ResultSchema11 id: 1, resource_num: 1, table_num: 1, vid: "col1", unit: "deg", datatype: "float", precision: "2", width: 6, ref: "J2000", name: "RA", ucd: "pos.eq.ra;meta.main", utype: nil, arraysize: nil, vtype: nil>
Note that in the above example the #vid method of the ResultSchema11 instance corresponds to the ID
attribute of the FIELD and the #vtype method corresponds to the FIELD’s type
attribute.
# when you're done, you can optionally delete any tables hanging around in the database
vot.cleanup()
# if the tables are already sitting around in the database (perhaps from a previous run)...
vot = ActiveVOTable::Base.from(:db, 'results')
ActiveVOTable::Base#from and ActiveVOTable::Base#cleanup both take an optional hash of connection parameters (the same kind ActiveRecord::Base#establish_connection does). This allows you the flexibility to upload your votables to different databases if desired. So something like this works:
vot1 = ActiveVOTable::Base.from(:xml,
File.new('results1.vot'),
'results1', # this *must* be different from below
:adapter => 'sqlite3', :database => 'votables.sqlite3'
)
vot2 = ActiveVOTable::Base.from(:xml,
File.new('results2.vot'),
'results2', # *must* be different from above
:adapter => 'mysql', :host => 'localhost', :username => 'me', :password => 'secret', :database => 'votables'
)
vot1.cleanup
vot2.cleanup
Constant Summary collapse
- SCHEMA_ID =
'schema'
- DATA_ID =
'data'
Class Method Summary collapse
-
.cleanup(remove_class_on_cleanup = false) {|_self| ... } ⇒ Object
Delete any tables and (optionally) Ruby constants associated with an already existing votable.
-
.data ⇒ Object
List all the data objects associated with the votable.
-
.dbtables(regex = nil) ⇒ Object
List the names of all the database tables associated with the votable in question.
-
.find_or_create_class(klass_name, subclass = Base) ⇒ Object
:nodoc:.
-
.from(src, *args) ⇒ Object
A convenience method around #from_xml and #from_database.
-
.from_database(name, conn_params = nil, logger = nil, tables_must_exist = true) ⇒ Object
Instantiate a votable from a database.
-
.from_xml(xml, name = nil, conn_params = nil, logger = nil) ⇒ Object
Instantiate a votable from XML.
-
.schemata ⇒ Object
List all the schema objects associated with the votable.
-
.table_search_pattern ⇒ Object
:nodoc:.
-
.tables ⇒ Object
List all the tables associated with the votable.
Class Method Details
.cleanup(remove_class_on_cleanup = false) {|_self| ... } ⇒ Object
Delete any tables and (optionally) Ruby constants associated with an already existing votable.
# ActiveVOTable::Base::Result11, ActiveVOTable::Base::Result11Schema
# and ActiveVOTable::Base::Result11Data are still hanging around after this
vot.cleanup
or…
# ActiveVOTable::Base::Result11, ActiveVOTable::Base::Result11Schema
# and ActiveVOTable::Base::Result11Data no longer exist...
vot.cleanup(true)
There is also a block form which allows you access to the classes after their database tables have been destroyed but before the classes themselves have been disposed of. This is mostly for testing purposes and very rarely used in real life.
vot.cleanup(true) do |v|
# the db tables are gone, but I can still play with the classes if I want...
end
420 421 422 423 424 |
# File 'lib/voruby/active_votable/active_votable.rb', line 420 def self.cleanup(remove_class_on_cleanup=false) self.dbtables.each { |t| self.connection.drop_table(t) } yield self if block_given? superclass.send(:remove_const, self.table_name.classify) if remove_class_on_cleanup and superclass.send(:const_defined?, self.table_name.classify) end |
.data ⇒ Object
List all the data objects associated with the votable. Typically, you’ll use #tables instead (which associates a schema with its corresponding data), but this is occassionally useful.
puts vot.data.inspect
# => [ActiveVOTable::Base::ResultData11]
374 375 376 377 378 379 380 381 |
# File 'lib/voruby/active_votable/active_votable.rb', line 374 def self.data self.dbtables(/_#{DATA_ID}_\d+_\d+$/).collect do |t| data_klass = self.find_or_create_class(t.classify, self) data_klass.extend(Pager) data_klass.set_table_name(t) data_klass end end |
.dbtables(regex = nil) ⇒ Object
List the names of all the database tables associated with the votable in question. And addition regex
may be given to further refine the list, if desired.
puts vot.dbtables.inspect
# => ['result_data_1_1', 'result_schema_1_1']
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 |
# File 'lib/voruby/active_votable/active_votable.rb', line 331 def self.dbtables(regex=nil) raise "#{connection.adapter_name} does not support #tables" if !connection.respond_to?(:tables) self.connection.tables.find_all{ |t| base_match = t.match(/^(#{self.table_name()})#{self.table_search_pattern()}$/) regex ? (base_match and t.match(regex)) : base_match }.sort { |a, b| a_matches = a.match(/^(#{self.table_name})#{self.table_search_pattern()}$/) b_matches = b.match(/^(#{self.table_name})#{self.table_search_pattern()}$/) a_matches[1] <=> b_matches[1] and a_matches[2] <=> a_matches[2] and a_matches[3].to_i <=> a_matches[3].to_i and b_matches[4].to_i <=> b_matches[4].to_i } end |
.find_or_create_class(klass_name, subclass = Base) ⇒ Object
:nodoc:
348 349 350 |
# File 'lib/voruby/active_votable/active_votable.rb', line 348 def self.find_or_create_class(klass_name, subclass=Base) #:nodoc: const_defined?(klass_name) ? const_get(klass_name) : const_set(klass_name, Class.new(subclass)) end |
.from(src, *args) ⇒ Object
A convenience method around #from_xml and #from_database. The connection parameters are only necessary if you haven’t previously called ActiveVOTable::Base#establish_connection.
ActiveVOTable::Base.from(:xml,
File.new('votable.xml'),
'my_votable'
:adapter => 'mysql', :host => 'localhost', :username => 'me', :password => 'secret', :database => 'votables',
)
ActiveVOTable::Base.from(:db,
'my_votable',
:adapter => 'mysql', :host => 'localhost', :username => 'me', :password => 'secret', :database => 'votables',
)
501 502 503 504 505 506 507 508 |
# File 'lib/voruby/active_votable/active_votable.rb', line 501 def self.from(src, *args) case src when :xml then self.from_xml(*args) when :db then self.from_database(*args) else raise "Source must one of: :xml, :db (not '#{src}')" end end |
.from_database(name, conn_params = nil, logger = nil, tables_must_exist = true) ⇒ Object
Instantiate a votable from a database.
Identical to #from_xml, except that it is assumed the appropriate tables already exist in the database.
vot = ActiveVOTable::Base.from_database(
'my_votable' # for those tables that begin with 'my_votable',
:adapter => 'mysql', :host => 'localhost', :username => 'me', :password => 'secret', :database => 'votables', # look in the MySQL database
)
474 475 476 477 478 479 480 481 482 483 484 485 |
# File 'lib/voruby/active_votable/active_votable.rb', line 474 def self.from_database(name, conn_params=nil, logger=nil, tables_must_exist=true) k = self.find_or_create_class(name.classify, Base) k.establish_connection(conn_params) if conn_params k.table_name = name k.logger = logger if logger k.inheritance_column = nil # 'type' is a common votable keyword, so we really want to turn simple inheritance off raise "tables corresponding to '#{name}' do not appear to exist" if tables_must_exist and k.dbtables.size == 0 k end |
.from_xml(xml, name = nil, conn_params = nil, logger = nil) ⇒ Object
Instantiate a votable from XML.
xml
-
May be a string or a File object.
name
-
A string representing the root names of the tables that will be created in the database. If none is specified the string ‘votable’ + a timestamp will be used.
dboptions
-
An optional hash of database connection parameters (exactly as you’d pass to ActiveRecord::Base#establish_connection). Necessary only if ActiveVOTable::Base#establish_connection hasn’t been called.
An array of objects representing the tables in the votables is returned. Each of these objects has a #data method and a #schema method corresponding to the TABLEDATA and FIELD elements in the VOTable specification.
vot = ActiveVOTable::Base.from_xml(
File.new('votable.xml'), # parse the file votable.xml
'my_votable' # every table created will be prefixed with 'my_votable',
:adapter => 'mysql', :host => 'localhost', :username => 'me', :password => 'secret', :database => 'votables', # stick it into a MySQL database
)
vot.each do |table|
puts table.schema.find(:all).inspect # retrieve rich semantic information about each column
puts table.data.find(:all, :conditions => ['ra > ?', 10.2]).inspect # find the actual data
end
Assuming votable.xml had 2 resources, each with one table (for example) the table structure created would look like: my_votable_fields_1_1, my_votable_rows_1_1, my_votable_fields_2_1, my_votable_rows_2_1
.
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 |
# File 'lib/voruby/active_votable/active_votable.rb', line 449 def self.from_xml(xml, name=nil, conn_params=nil, logger=nil) name ||= "votable_#{DateTime.now.strftime('%Y%m%d%H%M%S%L')}" raise "XML source must be a string or a File object, not '#{xml}'" if !xml.is_a?(String) and !xml.is_a?(File) parser = XML::SaxParser.new xml.is_a?(String) ? parser.string = xml : parser.filename = xml.path k = self.from_database(name, conn_params, logger, false) callbacks = Callbacks.new(k) parser.callbacks = callbacks parser.parse k end |
.schemata ⇒ Object
List all the schema objects associated with the votable. Typically, you’ll use #tables instead (which associates a schema with its corresponding data), but this is occassionally useful.
puts vot.schemata.inspect
# => [ActiveVOTable::Base::ResultSchema11]
359 360 361 362 363 364 365 |
# File 'lib/voruby/active_votable/active_votable.rb', line 359 def self.schemata self.dbtables(/_#{SCHEMA_ID}_\d+_\d+$/).collect do |t| schema_klass = self.find_or_create_class(t.classify, self) schema_klass.set_table_name(t) schema_klass end end |
.table_search_pattern ⇒ Object
:nodoc:
321 322 323 |
# File 'lib/voruby/active_votable/active_votable.rb', line 321 def self.table_search_pattern #:nodoc: "_(#{SCHEMA_ID}|#{DATA_ID})_(\\d+)_(\\d+)" end |
.tables ⇒ Object
List all the tables associated with the votable. Each member of the turned array responds to a #schema and #data method.
vot.tables.each do |t|
puts t.schema
puts t.data
end
391 392 393 394 395 396 397 398 399 |
# File 'lib/voruby/active_votable/active_votable.rb', line 391 def self.tables data_list = self.data list = [] self.schemata.each_with_index do |s, i| list << OpenStruct.new(:schema => s, :data => data_list[i]) end list end |