voruby 1.1.1 → 2.0.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.
- data/Rakefile.rb +107 -224
- data/lib/misc.rb +1 -0
- data/lib/misc/misc.rb +60 -0
- data/lib/misc/propertyfile.rb +31 -0
- data/lib/symphony.rb +1 -0
- data/lib/symphony/symphony.rb +247 -0
- data/lib/voruby.rb +186 -0
- data/lib/voruby/active_votable/active_votable.rb +468 -347
- data/lib/voruby/adql/1.0/adql.rb +2418 -0
- data/lib/voruby/adql/support.rb +2 -0
- data/lib/voruby/misc.rb +351 -0
- data/lib/voruby/misc/connection_monitor.rb +97 -0
- data/lib/voruby/misc/libxml_ext.rb +121 -0
- data/lib/voruby/misc/rexml_ext.rb +223 -0
- data/lib/voruby/resolver/resolver.rb +12 -0
- data/lib/voruby/resolver/sesame.rb +299 -0
- data/lib/voruby/sky_query/sky_query.rb +192 -0
- data/lib/voruby/stc/1.10/coords.rb +2272 -0
- data/lib/voruby/stc/1.10/region.rb +892 -0
- data/lib/voruby/stc/1.10/stc.rb +3271 -0
- data/lib/voruby/stc/1.30/stc.rb +8666 -0
- data/lib/voruby/stc/support.rb +2 -0
- data/lib/voruby/ucd/ucd.rb +173 -0
- data/lib/voruby/voevent/1.1/voevent.rb +1124 -0
- data/lib/voruby/voevent/support.rb +5 -0
- data/lib/voruby/votable/1.0/votable.rb +1807 -0
- data/lib/voruby/votable/1.1/votable.rb +2100 -0
- data/lib/voruby/votable/votable.rb +305 -0
- data/lib/voruby/wesix/wesix.rb +491 -0
- data/lib/voruby/xlink/1.2/xlink.rb +21 -0
- data/test/voruby/active_votable/complex.vot +60 -0
- data/test/voruby/active_votable/error.vot +6 -0
- data/test/voruby/active_votable/large.vot +130040 -0
- data/test/voruby/active_votable/simple1.vot +38 -0
- data/test/voruby/active_votable/simple2.vot +38 -0
- data/test/voruby/active_votable/test.rb +193 -0
- data/test/voruby/adql/1.0/adql-alias.sql +1 -0
- data/test/voruby/adql/1.0/adql-alias.xml +26 -0
- data/test/voruby/adql/1.0/adql-avg.sql +1 -0
- data/test/voruby/adql/1.0/adql-avg.xml +31 -0
- data/test/voruby/adql/1.0/adql-circle.sql +1 -0
- data/test/voruby/adql/1.0/adql-circle.xml +46 -0
- data/test/voruby/adql/1.0/adql-expr.sql +1 -0
- data/test/voruby/adql/1.0/adql-expr.xml +34 -0
- data/test/voruby/adql/1.0/adql-function.sql +1 -0
- data/test/voruby/adql/1.0/adql-function.xml +41 -0
- data/test/voruby/adql/1.0/adql-group.sql +1 -0
- data/test/voruby/adql/1.0/adql-group.xml +51 -0
- data/test/voruby/adql/1.0/adql-having.sql +1 -0
- data/test/voruby/adql/1.0/adql-having.xml +25 -0
- data/test/voruby/adql/1.0/adql-like.sql +1 -0
- data/test/voruby/adql/1.0/adql-like.xml +17 -0
- data/test/voruby/adql/1.0/adql-order.sql +1 -0
- data/test/voruby/adql/1.0/adql-order.xml +37 -0
- data/test/voruby/adql/1.0/adql-simple.sql +1 -0
- data/test/voruby/adql/1.0/adql-simple.xml +12 -0
- data/test/voruby/adql/1.0/adql-top.sql +1 -0
- data/test/voruby/adql/1.0/adql-top.xml +33 -0
- data/test/voruby/adql/1.0/test.rb +2220 -0
- data/test/voruby/misc/test.rb +32 -0
- data/test/voruby/resolver/sesame/test.rb +56 -0
- data/test/voruby/sky_query/test.rb +107 -0
- data/test/voruby/stc/1.10/coords_test.rb +3704 -0
- data/test/voruby/stc/1.10/region_test.rb +993 -0
- data/test/voruby/stc/1.10/stc-catalog-entry-location.xml +112 -0
- data/test/voruby/stc/1.10/stc-obs-data-location.xml +126 -0
- data/test/voruby/stc/1.10/stc-region-circle.xml +5 -0
- data/test/voruby/stc/1.10/stc-region-convex.xml +11 -0
- data/test/voruby/stc/1.10/stc-region-convexhull.xml +5 -0
- data/test/voruby/stc/1.10/stc-region-ellipse.xml +7 -0
- data/test/voruby/stc/1.10/stc-region-intersection.xml +25 -0
- data/test/voruby/stc/1.10/stc-region-negation.xml +7 -0
- data/test/voruby/stc/1.10/stc-region-polygon.xml +13 -0
- data/test/voruby/stc/1.10/stc-region-sector.xml +6 -0
- data/test/voruby/stc/1.10/stc-region-union.xml +25 -0
- data/test/voruby/stc/1.10/stc-resource-profile.xml +60 -0
- data/test/voruby/stc/1.10/stc-search-location.xml +54 -0
- data/test/voruby/stc/1.10/stc_test.rb +4626 -0
- data/test/voruby/stc/1.30/stc-catalog-entry-location.xml +210 -0
- data/test/voruby/stc/1.30/stc-obs-data-location-arecibo.xml +353 -0
- data/test/voruby/stc/1.30/stc-obs-data-location-fits.xml +250 -0
- data/test/voruby/stc/1.30/stc-obs-data-location-xlink.xml +63 -0
- data/test/voruby/stc/1.30/stc-obs-data-location.xml +216 -0
- data/test/voruby/stc/1.30/stc-resource-profile-unusual-ref-pos.xml +39 -0
- data/test/voruby/stc/1.30/stc-resource-profile.xml +129 -0
- data/test/voruby/stc/1.30/stc-search-location-arecibo.xml +86 -0
- data/test/voruby/stc/1.30/stc-search-location.xml +101 -0
- data/test/voruby/stc/1.30/test.rb +6274 -0
- data/test/voruby/ucd/test.rb +48 -0
- data/test/voruby/voevent/1.1/test.rb +812 -0
- data/test/{voevent/voevent_v1_1.xml → voruby/voevent/1.1/voevent.xml} +2 -2
- data/test/voruby/voregistry/0.3/test.rb +137 -0
- data/test/voruby/votable/1.0/test.rb +714 -0
- data/test/voruby/votable/1.0/votable.basic.xml +660 -0
- data/test/voruby/votable/1.0/votable.html +86 -0
- data/test/voruby/votable/1.0/votable.ns.xml +56 -0
- data/test/voruby/votable/1.1/test.rb +785 -0
- data/test/voruby/votable/1.1/votable.basic.xml +38 -0
- data/test/voruby/votable/1.1/votable.html +86 -0
- data/test/voruby/votable/1.1/votable.ns.xml +56 -0
- data/test/voruby/votable/test.rb +15 -0
- data/test/voruby/wesix/test.rb +268 -0
- data/test/voruby/wesix/testr.fits +28 -0
- metadata +234 -247
- data/REQUIREMENTS +0 -6
- data/lib/voruby/active_votable/loader.rb +0 -5
- data/lib/voruby/adql/adql.rb +0 -2787
- data/lib/voruby/adql/ext.rb +0 -14
- data/lib/voruby/adql/loader.rb +0 -6
- data/lib/voruby/adql/operations.rb +0 -54
- data/lib/voruby/adql/parser.rb +0 -160
- data/lib/voruby/adql/transforms.rb +0 -573
- data/lib/voruby/ext.rb +0 -17
- data/lib/voruby/loader.rb +0 -4
- data/lib/voruby/misc/propertyfile.rb +0 -36
- data/lib/voruby/plastic/applications.rb +0 -174
- data/lib/voruby/plastic/constants.rb +0 -30
- data/lib/voruby/plastic/loader.rb +0 -10
- data/lib/voruby/plastic/plastic.rb +0 -1
- data/lib/voruby/resources/conesearch/conesearch.rb +0 -9
- data/lib/voruby/resources/conesearch/conesearch_v0_2.rb +0 -55
- data/lib/voruby/resources/conesearch/conesearch_v0_3.rb +0 -50
- data/lib/voruby/resources/conesearch/conesearch_v1_0.rb +0 -72
- data/lib/voruby/resources/conesearch/loader.rb +0 -4
- data/lib/voruby/resources/loader.rb +0 -50
- data/lib/voruby/resources/nodes.rb +0 -190
- data/lib/voruby/resources/openskynode/loader.rb +0 -4
- data/lib/voruby/resources/openskynode/openskynode.rb +0 -9
- data/lib/voruby/resources/openskynode/openskynode_v0_1.rb +0 -54
- data/lib/voruby/resources/sia/loader.rb +0 -5
- data/lib/voruby/resources/sia/sia.rb +0 -9
- data/lib/voruby/resources/sia/sia_v0_6.rb +0 -90
- data/lib/voruby/resources/sia/sia_v0_7.rb +0 -89
- data/lib/voruby/resources/sia/sia_v1_0.rb +0 -122
- data/lib/voruby/resources/stsci.rb +0 -59
- data/lib/voruby/resources/vodataservice/coverage_v0_2.rb +0 -195
- data/lib/voruby/resources/vodataservice/coverage_v0_3.rb +0 -158
- data/lib/voruby/resources/vodataservice/loader.rb +0 -5
- data/lib/voruby/resources/vodataservice/vodataservice.rb +0 -9
- data/lib/voruby/resources/vodataservice/vodataservice_v0_4.rb +0 -189
- data/lib/voruby/resources/vodataservice/vodataservice_v0_5.rb +0 -163
- data/lib/voruby/resources/vodataservice/vodataservice_v1_0.rb +0 -221
- data/lib/voruby/resources/voregistry/loader.rb +0 -4
- data/lib/voruby/resources/voregistry/voregistry.rb +0 -9
- data/lib/voruby/resources/voregistry/voregistry_v0_2.rb +0 -40
- data/lib/voruby/resources/voregistry/voregistry_v0_3.rb +0 -30
- data/lib/voruby/resources/voregistry/voregistry_v1_0.rb +0 -86
- data/lib/voruby/resources/voresource/loader.rb +0 -17
- data/lib/voruby/resources/voresource/voresource.rb +0 -9
- data/lib/voruby/resources/voresource/voresource_v0_10.rb +0 -327
- data/lib/voruby/resources/voresource/voresource_v0_9.rb +0 -405
- data/lib/voruby/resources/voresource/voresource_v1_0.rb +0 -230
- data/lib/voruby/services/ext.rb +0 -11
- data/lib/voruby/services/gestalt/footprint.rb +0 -95
- data/lib/voruby/services/gestalt/wcs_fixer.rb +0 -105
- data/lib/voruby/services/gestalt/wesix.rb +0 -155
- data/lib/voruby/services/loader.rb +0 -7
- data/lib/voruby/services/registry/registry.rb +0 -53
- data/lib/voruby/services/resolver/resolver.rb +0 -35
- data/lib/voruby/services/schema/schema.rb +0 -644
- data/lib/voruby/sesame/loader.rb +0 -6
- data/lib/voruby/sesame/sesame_v1_0.rb +0 -64
- data/lib/voruby/simple/loader.rb +0 -6
- data/lib/voruby/simple/parameters.rb +0 -196
- data/lib/voruby/simple/sap.rb +0 -446
- data/lib/voruby/spacetime/loader.rb +0 -3
- data/lib/voruby/spacetime/spacetime.rb +0 -607
- data/lib/voruby/stc/coords_v1_20.rb +0 -900
- data/lib/voruby/stc/loader.rb +0 -55
- data/lib/voruby/stc/region_v1_20.rb +0 -274
- data/lib/voruby/stc/stc_v1_20.rb +0 -1196
- data/lib/voruby/util.rb +0 -27
- data/lib/voruby/voevent/loader.rb +0 -7
- data/lib/voruby/voevent/voevent_v1_0.rb +0 -213
- data/lib/voruby/voevent/voevent_v1_1.rb +0 -196
- data/lib/voruby/votables/chandra.rb +0 -373
- data/lib/voruby/votables/data.rb +0 -179
- data/lib/voruby/votables/galex.rb +0 -377
- data/lib/voruby/votables/int.rb +0 -354
- data/lib/voruby/votables/libxml_parser.rb +0 -411
- data/lib/voruby/votables/libxml_votable.rb +0 -67
- data/lib/voruby/votables/loader.rb +0 -10
- data/lib/voruby/votables/meta.rb +0 -763
- data/lib/voruby/votables/misc.rb +0 -51
- data/lib/voruby/votables/nsa.rb +0 -410
- data/lib/voruby/votables/rexml_parser.rb +0 -408
- data/lib/voruby/votables/rexml_votable.rb +0 -67
- data/lib/voruby/votables/sdss.rb +0 -356
- data/lib/voruby/votables/transforms.rb +0 -388
- data/lib/voruby/votables/tree.rb +0 -45
- data/lib/voruby/votables/types.rb +0 -391
- data/lib/voruby/votables/votable.rb +0 -687
- data/test/active_votable/database.yml +0 -6
- data/test/active_votable/test.vot +0 -168492
- data/test/active_votable/unittest.rb +0 -41
- data/test/adql/test1.adql +0 -49
- data/test/adql/test2.adql +0 -51
- data/test/adql/test3.adql +0 -81
- data/test/adql/test4.adql +0 -53
- data/test/adql/test5.adql +0 -55
- data/test/adql/test6.adql +0 -18
- data/test/adql/test7.adql +0 -48
- data/test/adql/unittest.rb +0 -1672
- data/test/plastic/test.rb +0 -44
- data/test/plastic/test.vot +0 -5385
- data/test/plastic/unittest.rb +0 -66
- data/test/resources/conesearch/conesearch_v0_3.xml +0 -31
- data/test/resources/conesearch/conesearch_v1_0.xml +0 -86
- data/test/resources/conesearch/unittest_v0_3.rb +0 -22
- data/test/resources/conesearch/unittest_v1_0.rb +0 -24
- data/test/resources/openskynode/open_sky_node_v0_1.xml +0 -32
- data/test/resources/openskynode/unittest_v0_1.rb +0 -31
- data/test/resources/sia/simple_image_access_v0_7.xml +0 -36
- data/test/resources/sia/simple_image_access_v1_0.xml +0 -122
- data/test/resources/sia/unittest_v0_7.rb +0 -24
- data/test/resources/sia/unittest_v1_0.rb +0 -29
- data/test/resources/stsci.xml +0 -336
- data/test/resources/unittest_stsci.rb +0 -25
- data/test/resources/vodataservice/catalog_service_resource_v1_0.xml +0 -128
- data/test/resources/vodataservice/data_collection_resource_v0_5.xml +0 -54
- data/test/resources/vodataservice/data_collection_resource_v1_0.xml +0 -117
- data/test/resources/vodataservice/data_service_resource_v1_0.xml +0 -115
- data/test/resources/vodataservice/sky_service_resource_v0_10.xml +0 -45
- data/test/resources/vodataservice/table_service_resource_v1_0.xml +0 -122
- data/test/resources/vodataservice/tabular_sky_service_resource_v0_10.xml +0 -60
- data/test/resources/vodataservice/unittest_v0_5.rb +0 -126
- data/test/resources/vodataservice/unittest_v1_0.rb +0 -151
- data/test/resources/voregistry/authority_resource_v0_3.xml +0 -20
- data/test/resources/voregistry/authority_resource_v1_0.xml +0 -82
- data/test/resources/voregistry/registry_service_v0_3.xml +0 -20
- data/test/resources/voregistry/registry_service_v1_0.xml +0 -107
- data/test/resources/voregistry/unittest_v0_3.rb +0 -31
- data/test/resources/voregistry/unittest_v1_0.rb +0 -34
- data/test/resources/voresource/organisation_resource_v1_0.xml +0 -90
- data/test/resources/voresource/resource_organisation_v0_10.xml +0 -22
- data/test/resources/voresource/resource_service_v0_10.xml +0 -19
- data/test/resources/voresource/resource_v0_10.xml +0 -19
- data/test/resources/voresource/resource_v1_0.xml +0 -79
- data/test/resources/voresource/service_resource_v1_0.xml +0 -91
- data/test/resources/voresource/unittest_v0_10.rb +0 -61
- data/test/resources/voresource/unittest_v0_9.rb +0 -4
- data/test/resources/voresource/unittest_v1_0.rb +0 -190
- data/test/services/gestalt/unittest.rb +0 -74
- data/test/services/registry/unittest.rb +0 -34
- data/test/services/resolver/unittest.rb +0 -38
- data/test/simple/unittest.rb +0 -46
- data/test/spacetime/unittest.rb +0 -39
- data/test/stc/catalog_entry_location_v1_20.xml +0 -112
- data/test/stc/obs_data_location_v1_20.xml +0 -108
- data/test/stc/search_location_v1_20.xml +0 -54
- data/test/stc/stc_resource_profile_v1_20.xml +0 -60
- data/test/stc/unittest_v1_20.rb +0 -620
- data/test/voevent/unittest_v1_0.rb +0 -79
- data/test/voevent/unittest_v1_1.rb +0 -70
- data/test/voevent/voevent_v1_0.xml +0 -96
- data/test/votables/test.vot +0 -366
- data/test/votables/unittest.rb +0 -54
@@ -1,391 +1,512 @@
|
|
1
|
-
require '
|
1
|
+
require 'ostruct'
|
2
2
|
|
3
|
-
|
3
|
+
begin
|
4
|
+
require 'rubygems'
|
5
|
+
rescue LoadError; end
|
6
|
+
require 'activerecord'
|
7
|
+
require 'xml/libxml'
|
4
8
|
|
5
9
|
module VORuby
|
6
|
-
module
|
10
|
+
module ActiveVOTable
|
11
|
+
|
12
|
+
class Callbacks #:nodoc:
|
13
|
+
include XML::SaxParser::Callbacks
|
7
14
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
self.
|
15
|
+
attr_accessor :base_class,
|
16
|
+
:meta_location, :data_location, :metadata,
|
17
|
+
:row, :data_tables, :fields_tables, :inside_td, :in_error
|
18
|
+
|
19
|
+
def initialize(base_class)
|
20
|
+
self.base_class = base_class
|
21
|
+
|
22
|
+
self.row = []
|
23
|
+
self.metadata = []
|
24
|
+
|
25
|
+
self.meta_location = OpenStruct.new(:resource => 0, :table => 0, :field => 0)
|
26
|
+
self.data_location = OpenStruct.new(:resource => 0, :table => 0, :row => 0, :column => 0)
|
27
|
+
self.inside_td = false
|
28
|
+
self.in_error = false
|
14
29
|
end
|
15
|
-
|
16
|
-
def
|
17
|
-
|
18
|
-
|
30
|
+
|
31
|
+
def on_start_element(name, attrs)
|
32
|
+
case name
|
33
|
+
when 'RESOURCE'
|
34
|
+
self.meta_location.resource += 1
|
35
|
+
self.data_location.resource += 1
|
36
|
+
self.meta_location.table = 0
|
37
|
+
self.data_location.table = 0
|
38
|
+
when 'TABLE'
|
39
|
+
self.meta_location.table += 1
|
40
|
+
self.meta_location.field = 0
|
41
|
+
|
42
|
+
self.data_location.table += 1
|
43
|
+
self.data_location.row = 0
|
44
|
+
self.data_location.column = 0
|
45
|
+
|
46
|
+
create_db_tables()
|
47
|
+
when 'FIELD'
|
48
|
+
self.meta_location.field += 1
|
49
|
+
self.metadata << attrs
|
50
|
+
when 'DATA'
|
51
|
+
create_data_schema() # create the table and make it available
|
52
|
+
create_metadata_table()
|
53
|
+
when 'TABLEDATA'
|
54
|
+
self.base_class.connection.begin_db_transaction()
|
55
|
+
when 'TR'
|
56
|
+
self.data_location.resource = self.meta_location.resource
|
57
|
+
self.data_location.table = self.meta_location.table
|
58
|
+
self.data_location.row += 1
|
59
|
+
self.data_location.column = 0
|
60
|
+
self.row.clear
|
61
|
+
when 'TD'
|
62
|
+
self.data_location.column += 1
|
63
|
+
self.inside_td = true
|
64
|
+
when 'INFO'
|
65
|
+
self.in_error = true if attrs['name'] == 'QUERY_STATUS' and attrs['value'] == 'ERROR'
|
66
|
+
end
|
19
67
|
end
|
20
|
-
|
21
|
-
def
|
22
|
-
|
68
|
+
|
69
|
+
def on_end_element(name)
|
70
|
+
case name
|
71
|
+
when 'TABLE'
|
72
|
+
self.metadata.clear
|
73
|
+
when 'TABLEDATA'
|
74
|
+
self.base_class.connection.commit_db_transaction()
|
75
|
+
when 'TR'
|
76
|
+
create_record()
|
77
|
+
when 'TD'
|
78
|
+
self.inside_td = false
|
79
|
+
when 'INFO'
|
80
|
+
self.in_error = false
|
81
|
+
end
|
23
82
|
end
|
24
|
-
|
25
|
-
def
|
26
|
-
|
83
|
+
|
84
|
+
def on_characters(chars)
|
85
|
+
raise "Query status error: #{chars}" if self.in_error
|
86
|
+
self.row << chars if self.inside_td
|
27
87
|
end
|
28
|
-
end
|
29
88
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
def initialize(file, actions={})
|
34
|
-
super(file)
|
35
|
-
|
36
|
-
@actions = actions
|
37
|
-
|
38
|
-
register_listeners()
|
89
|
+
def data_table_name
|
90
|
+
"#{self.base_class.table_name}_#{Base::DATA_ID}_#{self.data_location.resource}_#{self.data_location.table}"
|
39
91
|
end
|
40
|
-
|
41
|
-
def
|
42
|
-
self.
|
43
|
-
if self.actions.has_key?(:on_start_element) and self.actions[:on_start_element].has_key?(name)
|
44
|
-
self.actions[:on_start_element][name].call(name, attrs)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
self.add_listener(:on_end_element) do |name|
|
49
|
-
if self.actions.has_key?(:on_end_element) and self.actions[:on_end_element].has_key?(name)
|
50
|
-
self.actions[:on_end_element][name].call(name)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
self.add_listener(:on_characters) do |chars|
|
55
|
-
self.actions[:on_characters].call(chars) if self.actions.has_key?(:on_characters)
|
56
|
-
end
|
57
|
-
|
58
|
-
self.add_listener(:on_cdata_block) do |cdata|
|
59
|
-
self.actions[:on_cdata_block].call(cdata) if self.actions.has_key?(:on_cdata_block)
|
60
|
-
end
|
61
|
-
|
92
|
+
|
93
|
+
def fields_table_name
|
94
|
+
"#{self.base_class.table_name}_#{Base::SCHEMA_ID}_#{self.meta_location.resource}_#{self.meta_location.table}"
|
62
95
|
end
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
# :database => 'votables'
|
76
|
-
# :host => 'localhost'
|
77
|
-
# :username => 'dbuser'
|
78
|
-
# :password => 'dbpassword')
|
79
|
-
#
|
80
|
-
# ActiveVotable.build('test/active_votable/test.vot') do |vot|
|
81
|
-
# records = vot.find(:all, :conditions => ['survey LIKE ?', 'Deep Lens Survey'], :order => 'id')
|
82
|
-
#
|
83
|
-
# records = vot.page(10)
|
84
|
-
#
|
85
|
-
# vot.foreach_page do |page_num, records|
|
86
|
-
# puts "Page #{page_num}:"
|
87
|
-
# records.each do |rec|
|
88
|
-
# puts rec.inspect
|
89
|
-
# end
|
90
|
-
# end
|
91
|
-
class ActiveVotable < ActiveRecord::Base
|
92
|
-
@@end_of_cell = true
|
93
|
-
@@arow = []
|
94
|
-
@@ordered_columns = []
|
95
|
-
@@items_per_page = 20
|
96
|
-
@@added_col = false
|
97
|
-
|
98
|
-
DATATYPE_CONVERSIONS = {
|
99
|
-
'boolean' => :boolean,
|
100
|
-
'bit' => :integer,
|
101
|
-
'unsignedByte' => :integer,
|
102
|
-
'short' => :integer,
|
103
|
-
'int' => :integer,
|
104
|
-
'long' => :integer,
|
105
|
-
'char' => :string,
|
106
|
-
'unicodeChar' => :string,
|
107
|
-
'float' => :float,
|
108
|
-
'double' => :float,
|
109
|
-
'floatComplex' => :string,
|
110
|
-
'doubleComplex' => :string
|
111
|
-
}
|
112
|
-
|
113
|
-
@@actions = {
|
114
|
-
:on_start_element => {
|
115
|
-
'FIELD' => Proc.new { |name, attrs|
|
116
|
-
vot_col_name = attrs['name'] || attrs['ID'] || "unknown_column_#{rand(1000000)}"
|
117
|
-
field_name = ActiveVotable.columnize(vot_col_name)
|
118
|
-
field_name = 'record_id' if field_name == 'id'
|
119
|
-
field_type = ActiveVotable.column_type(attrs['datatype'] || 'char', attrs['arraysize'])
|
120
|
-
@@ordered_columns << {
|
121
|
-
:id => attrs['ID'],
|
122
|
-
:vot_col_name => vot_col_name, :name => field_name,
|
123
|
-
:datatype => attrs['datatype'], :type => field_type,
|
124
|
-
:ucd => attrs['ucd'],
|
125
|
-
:arraysize => attrs['arraysize']
|
126
|
-
}
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def create_db_tables
|
100
|
+
self.base_class.connection.create_table(data_table_name()){}
|
101
|
+
self.base_class.connection.create_table(fields_table_name()){}
|
102
|
+
end
|
103
|
+
|
104
|
+
def create_data_schema
|
105
|
+
# A couple additional columns representing the position of the table in the votable as a whole.
|
106
|
+
self.base_class.connection.add_column(self.data_table_name, :resource_num, :integer, :null => false, :default => 1)
|
107
|
+
self.base_class.connection.add_column(self.data_table_name, :table_num, :integer, :null => false, :default => 1)
|
127
108
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
'TABLEDATA' => Proc.new{ |name, attrs|
|
137
|
-
connection().begin_db_transaction()
|
138
|
-
}
|
139
|
-
},
|
140
|
-
:on_end_element => {
|
141
|
-
'TR' => Proc.new { |name|
|
142
|
-
row_def = {:resource_num => 1, :table_num => 1}
|
143
|
-
@@ordered_columns.each_index do |i|
|
144
|
-
row_def[@@ordered_columns[i][:name]] = cast(@@arow[i], @@ordered_columns[i][:type])
|
145
|
-
end
|
146
|
-
|
147
|
-
# On my machine, doing this rather than an ActiveRecord.create()
|
148
|
-
# and using begin_db_transaction() and commit_db_transaction()
|
149
|
-
# seems to speed things up by ~2x. The added complexity seemed worth
|
150
|
-
# it to me.
|
151
|
-
values = @@ordered_columns.collect{ |c| connection().quote(row_def[c[:name]]) }
|
152
|
-
connection().insert(
|
153
|
-
"INSERT INTO #{table_name()} " +
|
154
|
-
"(#{@@ordered_columns.collect{ |c| connection().quote_column_name(c[:name]) }.join(', ')}) " +
|
155
|
-
"VALUES (#{values.join(', ')})"
|
156
|
-
)
|
157
|
-
|
158
|
-
@@arow = []
|
159
|
-
},
|
160
|
-
'TD' => Proc.new { |name|
|
161
|
-
@@end_of_cell = true
|
162
|
-
@@arow << nil if !@@added_col # This is necessary because a blank tag doesn't trigger an on_characters callback.
|
163
|
-
},
|
164
|
-
'TABLEDATA' => Proc.new { |name|
|
165
|
-
connection().commit_db_transaction()
|
166
|
-
}
|
167
|
-
},
|
168
|
-
:on_characters => Proc.new { |chars|
|
169
|
-
@@arow << chars if !@@end_of_cell
|
170
|
-
@@added_col = true
|
171
|
-
},
|
172
|
-
:on_cdata_block => Proc.new{ |cdata|
|
173
|
-
@@arow << cdata if !@@end_of_cell # not sure if this is safe
|
174
|
-
}
|
175
|
-
}
|
176
|
-
|
177
|
-
# Parse a VOTable into a database table. If no table is specified, one will be
|
178
|
-
# created with the signature 'votable_timestamp_randomnumber'. If the optional
|
179
|
-
# block is provided, ActiveVotable.drop() will be called automatically.
|
180
|
-
# [_src_:]
|
181
|
-
# The name of the VOTable file.
|
182
|
-
# [_tbl_name_:]
|
183
|
-
# The name of the database table to dump the VOTable into.
|
184
|
-
def self.build(src, tbl_name=nil, &block)
|
185
|
-
if block
|
186
|
-
build(src, tbl_name)
|
187
|
-
block.call(self)
|
188
|
-
drop()
|
189
|
-
else
|
190
|
-
ActiveVotable.src = src
|
191
|
-
ActiveVotable.parser = VOTableExtractor.new(src, @@actions)
|
192
|
-
|
193
|
-
tname = tbl_name || "votable_#{Time.now.to_i}_#{rand(1000000)}"
|
194
|
-
ActiveVotable.table_name = tname
|
195
|
-
set_table_name(tname)
|
196
|
-
|
197
|
-
create_basic_schema()
|
198
|
-
parse()
|
199
|
-
|
200
|
-
return self
|
109
|
+
# The columns in the votable table itself.
|
110
|
+
self.metadata.each do |md|
|
111
|
+
column_name = (md['name'] || md['ID'] || '').downcase.gsub(/\W+/, '_')
|
112
|
+
#column_name = 'record_type' if column_name == 'type' # 'type' triggers ActiveRecord's
|
113
|
+
self.base_class.connection.add_column(
|
114
|
+
self.data_table_name,
|
115
|
+
column_name, db_data_type(md['datatype'], md['arraysize'])
|
116
|
+
)
|
201
117
|
end
|
118
|
+
|
119
|
+
# Add a simple index.
|
120
|
+
self.base_class.connection.add_index(self.data_table_name, [:resource_num, :table_num])
|
202
121
|
end
|
203
122
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
# The name of the database table the VOTable lives in.
|
209
|
-
def self.from_db_table(tbl_name, metadata=[], &block)
|
210
|
-
ActiveVotable.table_name = tbl_name
|
211
|
-
set_table_name(tbl_name)
|
212
|
-
@@ordered_columns = metadata
|
123
|
+
def create_metadata_table
|
124
|
+
# A couple additional columns representing the position of the table in the votable as a whole.
|
125
|
+
self.base_class.connection.add_column(self.fields_table_name, :resource_num, :integer, :null => false, :default => 1)
|
126
|
+
self.base_class.connection.add_column(self.fields_table_name, :table_num, :integer, :null => false, :default => 1)
|
213
127
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
128
|
+
# The columns defined by the votable table fields itself.
|
129
|
+
self.base_class.connection.add_column(self.fields_table_name, :vid, :string)
|
130
|
+
self.base_class.connection.add_column(self.fields_table_name, :unit, :string)
|
131
|
+
self.base_class.connection.add_column(self.fields_table_name, :datatype, :string)
|
132
|
+
self.base_class.connection.add_column(self.fields_table_name, :precision, :string)
|
133
|
+
self.base_class.connection.add_column(self.fields_table_name, :width, :integer)
|
134
|
+
self.base_class.connection.add_column(self.fields_table_name, :ref, :string)
|
135
|
+
self.base_class.connection.add_column(self.fields_table_name, :name, :string)
|
136
|
+
self.base_class.connection.add_column(self.fields_table_name, :ucd, :string)
|
137
|
+
self.base_class.connection.add_column(self.fields_table_name, :utype, :string)
|
138
|
+
self.base_class.connection.add_column(self.fields_table_name, :arraysize, :string)
|
139
|
+
self.base_class.connection.add_column(self.fields_table_name, :type, :string)
|
140
|
+
|
141
|
+
# A simple index.
|
142
|
+
self.base_class.connection.add_index(self.fields_table_name, [:resource_num, :table_num])
|
143
|
+
|
144
|
+
# and its columns
|
145
|
+
column_names = [
|
146
|
+
self.base_class.connection.quote_column_name('resource_num'),
|
147
|
+
self.base_class.connection.quote_column_name('table_num'),
|
148
|
+
self.base_class.connection.quote_column_name('vid'),
|
149
|
+
self.base_class.connection.quote_column_name('unit'),
|
150
|
+
self.base_class.connection.quote_column_name('datatype'),
|
151
|
+
self.base_class.connection.quote_column_name('precision'),
|
152
|
+
self.base_class.connection.quote_column_name('width'),
|
153
|
+
self.base_class.connection.quote_column_name('ref'),
|
154
|
+
self.base_class.connection.quote_column_name('name'),
|
155
|
+
self.base_class.connection.quote_column_name('ucd'),
|
156
|
+
self.base_class.connection.quote_column_name('utype'),
|
157
|
+
self.base_class.connection.quote_column_name('arraysize'),
|
158
|
+
self.base_class.connection.quote_column_name('type')
|
159
|
+
]
|
160
|
+
|
161
|
+
self.base_class.connection.begin_db_transaction()
|
162
|
+
|
163
|
+
columns = self.base_class.connection.columns(self.fields_table_name)
|
164
|
+
self.metadata.each do |md|
|
165
|
+
# quote each value
|
166
|
+
column_values = [
|
167
|
+
self.base_class.connection.quote(self.meta_location.resource, columns.find{ |c| c.name == 'resource_num'}),
|
168
|
+
self.base_class.connection.quote(self.meta_location.table, columns.find{ |c| c.name == 'table_num' }),
|
169
|
+
self.base_class.connection.quote(md['ID'], columns.find{ |c| c.name == 'vid'}),
|
170
|
+
self.base_class.connection.quote(md['unit'], columns.find{ |c| c.name == 'unit'}),
|
171
|
+
self.base_class.connection.quote(md['datatype'], columns.find{ |c| c.name == 'datatype'}),
|
172
|
+
self.base_class.connection.quote(md['precision'], columns.find{ |c| c.name == 'precision'}),
|
173
|
+
self.base_class.connection.quote(md['width'], columns.find{ |c| c.name == 'width'}),
|
174
|
+
self.base_class.connection.quote(md['ref'], columns.find{ |c| c.name == 'ref'}),
|
175
|
+
self.base_class.connection.quote(md['name'], columns.find{ |c| c.name == 'name'}),
|
176
|
+
self.base_class.connection.quote(md['ucd'], columns.find{ |c| c.name == 'ucd'}),
|
177
|
+
self.base_class.connection.quote(md['utype'], columns.find{ |c| c.name == 'utype'}),
|
178
|
+
self.base_class.connection.quote(md['arraysize'], columns.find{ |c| c.name == 'arraysize'}),
|
179
|
+
self.base_class.connection.quote(md['type'], columns.find{ |c| c.name == 'type'})
|
180
|
+
]
|
181
|
+
|
182
|
+
column_names = columns.reject{ |c| c.name == 'id' }.collect{ |c| self.base_class.connection.quote_column_name(c.name) }
|
183
|
+
self.base_class.connection.insert(
|
184
|
+
"INSERT INTO #{self.base_class.connection.quote_table_name(self.fields_table_name)} (#{column_names.join(', ')}) VALUES (#{column_values.join(', ')})"
|
185
|
+
)
|
219
186
|
end
|
187
|
+
|
188
|
+
self.base_class.connection.commit_db_transaction()
|
220
189
|
end
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
190
|
+
|
191
|
+
def create_record
|
192
|
+
column_names = [
|
193
|
+
self.base_class.connection.quote_column_name('resource_num'),
|
194
|
+
self.base_class.connection.quote_column_name('table_num')
|
195
|
+
]
|
196
|
+
column_values = [
|
197
|
+
self.base_class.connection.quote(self.data_location.resource),
|
198
|
+
self.base_class.connection.quote(self.data_location.table)
|
199
|
+
]
|
200
|
+
|
201
|
+
# The first three columns are id (which is auto-incremented),
|
202
|
+
# resource_num and table_num (which we've taken care of above).
|
203
|
+
self.base_class.connection.columns(self.data_table_name)[3..-1].each_with_index { |c, i|
|
204
|
+
column_values << self.base_class.connection.quote(self.row[i], c)
|
205
|
+
column_names << self.base_class.connection.quote_column_name(c.name)
|
206
|
+
}
|
207
|
+
|
208
|
+
self.base_class.connection.insert(
|
209
|
+
"INSERT INTO #{self.base_class.connection.quote_table_name(self.data_table_name)} (#{column_names.join(', ')}) VALUES (#{column_values.join(', ')})"
|
210
|
+
)
|
236
211
|
end
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
212
|
+
|
213
|
+
def db_data_type(xml_datatype, xml_arraysize=nil)
|
214
|
+
return :string if xml_arraysize # cop out if the arraysize is specified
|
215
|
+
|
216
|
+
case xml_datatype
|
217
|
+
when 'boolean' then :boolean
|
218
|
+
when 'bit' then :integer
|
219
|
+
when 'unsignedByte' then :text
|
220
|
+
when 'short' then :integer
|
221
|
+
when 'int' then :integer
|
222
|
+
when 'log' then :float
|
223
|
+
when 'char' then :string
|
224
|
+
when 'unicodeChar' then :string
|
225
|
+
when 'float' then :float
|
226
|
+
when 'double' then :float
|
227
|
+
when 'floatComplex' then :string
|
228
|
+
when 'doubleComplex' then :string
|
229
|
+
else
|
230
|
+
:string
|
231
|
+
end
|
241
232
|
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# Paging methods for data.
|
236
|
+
module Pager
|
237
|
+
DEFAULT_PER_PAGE = 25
|
242
238
|
|
243
|
-
|
244
|
-
|
245
|
-
def self.table_name=(tbl_name)
|
246
|
-
@@table_name = tbl_name
|
239
|
+
def per_page=(n)
|
240
|
+
@per_page = n
|
247
241
|
end
|
248
242
|
|
249
|
-
|
250
|
-
|
251
|
-
@@table_name
|
252
|
-
end
|
253
|
-
|
254
|
-
# Set the number of records per page for use in paging results.
|
255
|
-
def self.items_per_page=(num)
|
256
|
-
@@items_per_page = num
|
257
|
-
end
|
258
|
-
|
259
|
-
# Get the number of records per page.
|
260
|
-
def self.items_per_page
|
261
|
-
@@items_per_page
|
243
|
+
def per_page
|
244
|
+
@per_page || DEFAULT_PER_PAGE
|
262
245
|
end
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
246
|
+
|
247
|
+
def page(p=1, page_size=nil, options={})
|
248
|
+
options ||= {}
|
249
|
+
|
250
|
+
options[:limit] = page_size || per_page
|
251
|
+
options[:offset] = options[:limit] * (p - 1)
|
252
|
+
|
253
|
+
find(:all, options)
|
268
254
|
end
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
create_table ActiveVotable::table_name(), :primary_key => 'id' do |t|
|
276
|
-
t.column :resource_num, :integer, :null => false, :default => 1
|
277
|
-
t.column :table_num, :integer, :null => false, :default => 1
|
278
|
-
end
|
255
|
+
|
256
|
+
def each_page(page_size=nil, options={})
|
257
|
+
num_per_page = page_size || per_page
|
258
|
+
|
259
|
+
(1..(count().to_f / num_per_page.to_f).ceil()).each do |n|
|
260
|
+
yield(page(n, num_per_page), n)
|
279
261
|
end
|
280
262
|
end
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
263
|
+
end
|
264
|
+
|
265
|
+
# Have you ever wished you could treat your VOTable[http://www.ivoa.net/Documents/latest/VOT.html]
|
266
|
+
# as a database? Well, now you can. ActiveVOTable is a package for reading votables into
|
267
|
+
# a relational database. It uses the SAX parser in LibXML[http://libxml.rubyforge.org/] so it can
|
268
|
+
# handle votables of arbitrary size, and it wraps the resulting database tables in ActiveRecord[http://ar.rubyonrails.com/]
|
269
|
+
# so that you can easily execute queries against it. It can even handle votables with multiple tables
|
270
|
+
# in multiple resources.
|
271
|
+
#
|
272
|
+
# ActiveVOTable::Base.logger = Logger.new(STDOUT) # set the logger to output to STDOUT, exactly as in ActiveRecord
|
273
|
+
# ActiveVOTable::Base.establish_connection(:adapter => 'sqlite3', :database => 'votables.sqlite3') # connect to the database
|
274
|
+
#
|
275
|
+
# # read in the file results.vot into a SQLite database
|
276
|
+
# vot = ActiveVOTable::Base.from(:xml, File.new('results.vot'), 'results')
|
277
|
+
#
|
278
|
+
# table = vot.first # a votable can have more than one TABLE element, but we're interested in the first one.
|
279
|
+
#
|
280
|
+
# # every table has a data section and schema section, both of which are accessible
|
281
|
+
# data = table.data
|
282
|
+
# schema = table.schema
|
283
|
+
#
|
284
|
+
# # both data and schemata are ultimately simply subclasses of ActiveRecord::Base and have all the same methods
|
285
|
+
# puts data.find(:first).inspect
|
286
|
+
# # => <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>
|
287
|
+
# puts schema.find(:first).inspect
|
288
|
+
# # => #<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>
|
289
|
+
#
|
290
|
+
# Note that in the above example the #vid method of the ResultSchema11 instance corresponds to the <tt>ID</tt> attribute of the FIELD
|
291
|
+
# and the #vtype method corresponds to the FIELD's <tt>type</tt> attribute.
|
292
|
+
#
|
293
|
+
# # when you're done, you can optionally delete any tables hanging around in the database
|
294
|
+
# vot.cleanup()
|
295
|
+
#
|
296
|
+
# # if the tables are already sitting around in the database (perhaps from a previous run)...
|
297
|
+
# vot = ActiveVOTable::Base.from(:db, 'results')
|
298
|
+
#
|
299
|
+
# ActiveVOTable::Base#from and ActiveVOTable::Base#cleanup both take an optional hash of connection parameters
|
300
|
+
# (the same kind ActiveRecord::Base#establish_connection does). This allows you the flexibility to upload
|
301
|
+
# your votables to different databases if desired. So something like this works:
|
302
|
+
#
|
303
|
+
# vot1 = ActiveVOTable::Base.from(:xml,
|
304
|
+
# File.new('results1.vot'),
|
305
|
+
# 'results1', # this *must* be different from below
|
306
|
+
# :adapter => 'sqlite3', :database => 'votables.sqlite3'
|
307
|
+
# )
|
308
|
+
#
|
309
|
+
# vot2 = ActiveVOTable::Base.from(:xml,
|
310
|
+
# File.new('results2.vot'),
|
311
|
+
# 'results2', # *must* be different from above
|
312
|
+
# :adapter => 'mysql', :host => 'localhost', :username => 'me', :password => 'secret', :database => 'votables'
|
313
|
+
# )
|
314
|
+
#
|
315
|
+
# vot1.cleanup
|
316
|
+
# vot2.cleanup
|
317
|
+
class Base < ActiveRecord::Base
|
318
|
+
SCHEMA_ID = 'schema'
|
319
|
+
DATA_ID = 'data'
|
320
|
+
|
321
|
+
def self.table_search_pattern #:nodoc:
|
322
|
+
"_(#{SCHEMA_ID}|#{DATA_ID})_(\\d+)_(\\d+)"
|
323
|
+
end
|
324
|
+
|
325
|
+
# List the names of all the database tables associated with the
|
326
|
+
# votable in question. And addition <tt>regex</tt> may be given
|
327
|
+
# to further refine the list, if desired.
|
328
|
+
#
|
329
|
+
# puts vot.dbtables.inspect
|
330
|
+
# # => ['result_data_1_1', 'result_schema_1_1']
|
331
|
+
def self.dbtables(regex=nil)
|
332
|
+
raise "#{connection.adapter_name} does not support #tables" if !connection.respond_to?(:tables)
|
333
|
+
|
334
|
+
self.connection.tables.find_all{ |t|
|
335
|
+
base_match = t.match(/^(#{self.table_name()})#{self.table_search_pattern()}$/)
|
336
|
+
regex ? (base_match and t.match(regex)) : base_match
|
337
|
+
}.sort { |a, b|
|
338
|
+
a_matches = a.match(/^(#{self.table_name})#{self.table_search_pattern()}$/)
|
339
|
+
b_matches = b.match(/^(#{self.table_name})#{self.table_search_pattern()}$/)
|
340
|
+
|
341
|
+
a_matches[1] <=> b_matches[1] and
|
342
|
+
a_matches[2] <=> a_matches[2] and
|
343
|
+
a_matches[3].to_i <=> a_matches[3].to_i and
|
344
|
+
b_matches[4].to_i <=> b_matches[4].to_i
|
345
|
+
}
|
291
346
|
end
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
# directly.
|
296
|
-
def self.columnize(name)
|
297
|
-
name = name.downcase
|
298
|
-
name['-'] = '_' if name =~ /-/
|
299
|
-
name[/\W+/] = '_' if name =~ /\W+/
|
300
|
-
name
|
347
|
+
|
348
|
+
def self.find_or_create_class(klass_name, subclass=Base) #:nodoc:
|
349
|
+
const_defined?(klass_name) ? const_get(klass_name) : const_set(klass_name, Class.new(subclass))
|
301
350
|
end
|
302
|
-
|
303
|
-
#
|
304
|
-
#
|
305
|
-
#
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
351
|
+
|
352
|
+
# List all the schema objects associated with the votable.
|
353
|
+
# Typically, you'll use #tables instead (which associates
|
354
|
+
# a schema with its corresponding data), but this is occassionally
|
355
|
+
# useful.
|
356
|
+
#
|
357
|
+
# puts vot.schemata.inspect
|
358
|
+
# # => [ActiveVOTable::Base::ResultSchema11]
|
359
|
+
def self.schemata
|
360
|
+
self.dbtables(/_#{SCHEMA_ID}_\d+_\d+$/).collect do |t|
|
361
|
+
schema_klass = self.find_or_create_class(t.classify, self)
|
362
|
+
schema_klass.set_table_name(t)
|
363
|
+
schema_klass
|
311
364
|
end
|
312
365
|
end
|
313
|
-
|
314
|
-
#
|
315
|
-
#
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
366
|
+
|
367
|
+
# List all the data objects associated with the votable.
|
368
|
+
# Typically, you'll use #tables instead (which associates
|
369
|
+
# a schema with its corresponding data), but this is occassionally
|
370
|
+
# useful.
|
371
|
+
#
|
372
|
+
# puts vot.data.inspect
|
373
|
+
# # => [ActiveVOTable::Base::ResultData11]
|
374
|
+
def self.data
|
375
|
+
self.dbtables(/_#{DATA_ID}_\d+_\d+$/).collect do |t|
|
376
|
+
data_klass = self.find_or_create_class(t.classify, self)
|
377
|
+
data_klass.extend(Pager)
|
378
|
+
data_klass.set_table_name(t)
|
379
|
+
data_klass
|
324
380
|
end
|
325
381
|
end
|
326
|
-
|
327
|
-
#
|
328
|
-
#
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
#
|
334
|
-
#
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
options[:offset] = items_per_page() * (options[:page] - 1)
|
342
|
-
options.delete(:page)
|
343
|
-
end
|
344
|
-
|
345
|
-
validate_find_options(options)
|
346
|
-
set_readonly_option!(options)
|
347
|
-
|
348
|
-
case args.first
|
349
|
-
when :first then find_initial(options)
|
350
|
-
when :all then find_every(options)
|
351
|
-
else find_from_ids(args, options)
|
382
|
+
|
383
|
+
# List all the tables associated with the votable.
|
384
|
+
# Each member of the turned array responds to a #schema and
|
385
|
+
# #data method.
|
386
|
+
#
|
387
|
+
# vot.tables.each do |t|
|
388
|
+
# puts t.schema
|
389
|
+
# puts t.data
|
390
|
+
# end
|
391
|
+
def self.tables
|
392
|
+
data_list = self.data
|
393
|
+
|
394
|
+
list = []
|
395
|
+
self.schemata.each_with_index do |s, i|
|
396
|
+
list << OpenStruct.new(:schema => s, :data => data_list[i])
|
352
397
|
end
|
353
|
-
|
354
|
-
|
355
|
-
# Retrieve the specified page. Applies to
|
356
|
-
# all rows in the VOTable.
|
357
|
-
def self.page(page=1)
|
358
|
-
find(:all, :order => 'id', :page => page)
|
398
|
+
list
|
359
399
|
end
|
360
400
|
|
361
|
-
#
|
362
|
-
|
363
|
-
|
401
|
+
# Delete any tables and (optionally) Ruby constants associated with an already existing votable.
|
402
|
+
#
|
403
|
+
# # ActiveVOTable::Base::Result11, ActiveVOTable::Base::Result11Schema
|
404
|
+
# # and ActiveVOTable::Base::Result11Data are still hanging around after this
|
405
|
+
# vot.cleanup
|
406
|
+
#
|
407
|
+
# or...
|
408
|
+
#
|
409
|
+
# # ActiveVOTable::Base::Result11, ActiveVOTable::Base::Result11Schema
|
410
|
+
# # and ActiveVOTable::Base::Result11Data no longer exist...
|
411
|
+
# vot.cleanup(true)
|
412
|
+
#
|
413
|
+
# There is also a block form which allows you access to the classes after their database
|
414
|
+
# tables have been destroyed but before the classes themselves have been disposed of. This
|
415
|
+
# is mostly for testing purposes and very rarely used in real life.
|
416
|
+
#
|
417
|
+
# vot.cleanup(true) do |v|
|
418
|
+
# # the db tables are gone, but I can still play with the classes if I want...
|
419
|
+
# end
|
420
|
+
def self.cleanup(remove_class_on_cleanup=false)
|
421
|
+
self.dbtables.each { |t| self.connection.drop_table(t) }
|
422
|
+
yield self if block_given?
|
423
|
+
superclass.send(:remove_const, self.table_name.classify) if remove_class_on_cleanup and superclass.send(:const_defined?, self.table_name.classify)
|
364
424
|
end
|
425
|
+
|
426
|
+
# Instantiate a votable from XML.
|
427
|
+
#
|
428
|
+
# +xml+:: May be a string or a File object.
|
429
|
+
# +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.
|
430
|
+
# +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.
|
431
|
+
#
|
432
|
+
# An array of objects representing the tables in the votables is returned.
|
433
|
+
# Each of these objects has a #data method and a #schema method corresponding
|
434
|
+
# to the TABLEDATA and FIELD elements in the VOTable specification.
|
435
|
+
#
|
436
|
+
# vot = ActiveVOTable::Base.from_xml(
|
437
|
+
# File.new('votable.xml'), # parse the file votable.xml
|
438
|
+
# 'my_votable' # every table created will be prefixed with 'my_votable',
|
439
|
+
# :adapter => 'mysql', :host => 'localhost', :username => 'me', :password => 'secret', :database => 'votables', # stick it into a MySQL database
|
440
|
+
# )
|
441
|
+
#
|
442
|
+
# vot.each do |table|
|
443
|
+
# puts table.schema.find(:all).inspect # retrieve rich semantic information about each column
|
444
|
+
# puts table.data.find(:all, :conditions => ['ra > ?', 10.2]).inspect # find the actual data
|
445
|
+
# end
|
446
|
+
#
|
447
|
+
# Assuming votable.xml had 2 resources, each with one table (for example) the table structure created
|
448
|
+
# would look like: <tt>my_votable_fields_1_1, my_votable_rows_1_1, my_votable_fields_2_1, my_votable_rows_2_1</tt>.
|
449
|
+
def self.from_xml(xml, name=nil, conn_params=nil, logger=nil)
|
450
|
+
name ||= "votable_#{DateTime.now.strftime('%Y%m%d%H%M%S%L')}"
|
451
|
+
raise "XML source must be a string or a File object, not '#{xml}'" if !xml.is_a?(String) and !xml.is_a?(File)
|
452
|
+
|
453
|
+
parser = XML::SaxParser.new
|
454
|
+
xml.is_a?(String) ? parser.string = xml : parser.filename = xml.path
|
455
|
+
|
456
|
+
k = self.from_database(name, conn_params, logger, false)
|
457
|
+
|
458
|
+
callbacks = Callbacks.new(k)
|
459
|
+
parser.callbacks = callbacks
|
460
|
+
parser.parse
|
365
461
|
|
366
|
-
|
367
|
-
# block receives the page number and the list of
|
368
|
-
# records.
|
369
|
-
def self.foreach_page(&block)
|
370
|
-
num_pages = (count().to_f / items_per_page().to_f).ceil()
|
371
|
-
|
372
|
-
(1..num_pages).each do |page_num|
|
373
|
-
block.call(page_num, page(page_num))
|
374
|
-
end
|
462
|
+
k
|
375
463
|
end
|
376
|
-
|
377
|
-
#
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
464
|
+
|
465
|
+
# Instantiate a votable from a database.
|
466
|
+
#
|
467
|
+
# Identical to #from_xml, except that it is assumed the appropriate tables already
|
468
|
+
# exist in the database.
|
469
|
+
#
|
470
|
+
# vot = ActiveVOTable::Base.from_database(
|
471
|
+
# 'my_votable' # for those tables that begin with 'my_votable',
|
472
|
+
# :adapter => 'mysql', :host => 'localhost', :username => 'me', :password => 'secret', :database => 'votables', # look in the MySQL database
|
473
|
+
# )
|
474
|
+
def self.from_database(name, conn_params=nil, logger=nil, tables_must_exist=true)
|
475
|
+
k = self.find_or_create_class(name.classify, Base)
|
476
|
+
|
477
|
+
k.establish_connection(conn_params) if conn_params
|
478
|
+
k.table_name = name
|
479
|
+
k.logger = logger if logger
|
480
|
+
k.inheritance_column = nil # 'type' is a common votable keyword, so we really want to turn simple inheritance off
|
481
|
+
|
482
|
+
raise "tables corresponding to '#{name}' do not appear to exist" if tables_must_exist and k.dbtables.size == 0
|
483
|
+
|
484
|
+
k
|
382
485
|
end
|
383
486
|
|
384
|
-
|
385
|
-
|
487
|
+
# A convenience method around #from_xml and #from_database.
|
488
|
+
# The connection parameters are only necessary if you haven't previously called
|
489
|
+
# ActiveVOTable::Base#establish_connection.
|
490
|
+
#
|
491
|
+
# ActiveVOTable::Base.from(:xml,
|
492
|
+
# File.new('votable.xml'),
|
493
|
+
# 'my_votable'
|
494
|
+
# :adapter => 'mysql', :host => 'localhost', :username => 'me', :password => 'secret', :database => 'votables',
|
495
|
+
# )
|
496
|
+
#
|
497
|
+
# ActiveVOTable::Base.from(:db,
|
498
|
+
# 'my_votable',
|
499
|
+
# :adapter => 'mysql', :host => 'localhost', :username => 'me', :password => 'secret', :database => 'votables',
|
500
|
+
# )
|
501
|
+
def self.from(src, *args)
|
502
|
+
case src
|
503
|
+
when :xml then self.from_xml(*args)
|
504
|
+
when :db then self.from_database(*args)
|
505
|
+
else
|
506
|
+
raise "Source must one of: :xml, :db (not '#{src}')"
|
507
|
+
end
|
386
508
|
end
|
387
|
-
|
388
509
|
end
|
510
|
+
|
389
511
|
end
|
390
|
-
end
|
391
|
-
|
512
|
+
end
|