rets-hack 0.11
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +142 -0
- data/Manifest.txt +58 -0
- data/README.md +129 -0
- data/Rakefile +28 -0
- data/bin/rets +202 -0
- data/example/connect.rb +19 -0
- data/example/get-photos.rb +20 -0
- data/example/get-property.rb +16 -0
- data/lib/rets/client.rb +373 -0
- data/lib/rets/client_progress_reporter.rb +48 -0
- data/lib/rets/http_client.rb +133 -0
- data/lib/rets/locking_http_client.rb +34 -0
- data/lib/rets/measuring_http_client.rb +27 -0
- data/lib/rets/metadata/caching.rb +59 -0
- data/lib/rets/metadata/containers.rb +89 -0
- data/lib/rets/metadata/file_cache.rb +29 -0
- data/lib/rets/metadata/json_serializer.rb +27 -0
- data/lib/rets/metadata/lookup_table.rb +65 -0
- data/lib/rets/metadata/lookup_type.rb +19 -0
- data/lib/rets/metadata/marshal_serializer.rb +27 -0
- data/lib/rets/metadata/multi_lookup_table.rb +70 -0
- data/lib/rets/metadata/null_cache.rb +24 -0
- data/lib/rets/metadata/resource.rb +103 -0
- data/lib/rets/metadata/rets_class.rb +57 -0
- data/lib/rets/metadata/rets_object.rb +41 -0
- data/lib/rets/metadata/root.rb +155 -0
- data/lib/rets/metadata/table.rb +33 -0
- data/lib/rets/metadata/table_factory.rb +19 -0
- data/lib/rets/metadata/yaml_serializer.rb +27 -0
- data/lib/rets/metadata.rb +18 -0
- data/lib/rets/parser/compact.rb +117 -0
- data/lib/rets/parser/error_checker.rb +56 -0
- data/lib/rets/parser/multipart.rb +39 -0
- data/lib/rets.rb +269 -0
- data/test/fixtures.rb +324 -0
- data/test/helper.rb +14 -0
- data/test/test_caching.rb +89 -0
- data/test/test_client.rb +307 -0
- data/test/test_error_checker.rb +87 -0
- data/test/test_file_cache.rb +42 -0
- data/test/test_http_client.rb +132 -0
- data/test/test_json_serializer.rb +26 -0
- data/test/test_locking_http_client.rb +29 -0
- data/test/test_marshal_serializer.rb +26 -0
- data/test/test_metadata.rb +71 -0
- data/test/test_metadata_class.rb +50 -0
- data/test/test_metadata_lookup_table.rb +21 -0
- data/test/test_metadata_lookup_type.rb +21 -0
- data/test/test_metadata_multi_lookup_table.rb +60 -0
- data/test/test_metadata_object.rb +33 -0
- data/test/test_metadata_resource.rb +148 -0
- data/test/test_metadata_root.rb +151 -0
- data/test/test_metadata_table.rb +21 -0
- data/test/test_metadata_table_factory.rb +24 -0
- data/test/test_parser_compact.rb +115 -0
- data/test/test_parser_multipart.rb +39 -0
- data/test/test_yaml_serializer.rb +26 -0
- data/test/vcr_cassettes/unauthorized_response.yml +262 -0
- metadata +227 -0
@@ -0,0 +1,89 @@
|
|
1
|
+
module Rets
|
2
|
+
module Metadata
|
3
|
+
#########################
|
4
|
+
# Basic representation of the underlying metadata. This models
|
5
|
+
# the structure of RETS metadata closely. The OO-representation
|
6
|
+
# uses this structure for its construction. External usage of
|
7
|
+
# this API should be discouraged in favor of the richer OO
|
8
|
+
# representation.
|
9
|
+
#
|
10
|
+
module Containers
|
11
|
+
class Container
|
12
|
+
attr_accessor :fragment
|
13
|
+
|
14
|
+
def self.uses(*fields)
|
15
|
+
fields.each do |field|
|
16
|
+
define_method(field) do
|
17
|
+
fields_hash[field] || fields_hash[field] = extract(fragment, field.to_s.capitalize)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
uses :date, :version
|
23
|
+
|
24
|
+
def initialize(fragment)
|
25
|
+
self.fragment = fragment
|
26
|
+
end
|
27
|
+
|
28
|
+
def extract(fragment, attr)
|
29
|
+
fragment.attr(attr)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def fields_hash
|
35
|
+
@fields ||= {}
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
class RowContainer < Container
|
41
|
+
|
42
|
+
attr_accessor :rows
|
43
|
+
|
44
|
+
def initialize(doc)
|
45
|
+
super
|
46
|
+
self.rows = Parser::Compact.parse_document(doc)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
class ResourceContainer < RowContainer
|
52
|
+
alias resources rows
|
53
|
+
end
|
54
|
+
|
55
|
+
class ClassContainer < RowContainer
|
56
|
+
uses :resource
|
57
|
+
|
58
|
+
alias classes rows
|
59
|
+
end
|
60
|
+
|
61
|
+
class TableContainer < RowContainer
|
62
|
+
uses :resource, :class
|
63
|
+
|
64
|
+
alias tables rows
|
65
|
+
end
|
66
|
+
|
67
|
+
class LookupContainer < RowContainer
|
68
|
+
uses :resource
|
69
|
+
|
70
|
+
alias lookups rows
|
71
|
+
end
|
72
|
+
|
73
|
+
class LookupTypeContainer < RowContainer
|
74
|
+
uses :resource, :lookup
|
75
|
+
|
76
|
+
alias lookup_types rows
|
77
|
+
end
|
78
|
+
|
79
|
+
class ObjectContainer < RowContainer
|
80
|
+
uses :resource
|
81
|
+
|
82
|
+
alias objects rows
|
83
|
+
end
|
84
|
+
|
85
|
+
class SystemContainer < Container
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Rets
|
2
|
+
module Metadata
|
3
|
+
|
4
|
+
# This metadata cache persists the metadata to a file.
|
5
|
+
class FileCache
|
6
|
+
|
7
|
+
def initialize(path)
|
8
|
+
@path = path
|
9
|
+
end
|
10
|
+
|
11
|
+
# Save the metadata. Should yield an IO-like object to a block;
|
12
|
+
# that block will serialize the metadata to that object.
|
13
|
+
def save(&block)
|
14
|
+
File.open(@path, "wb", &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Load the metadata. Should yield an IO-like object to a block;
|
18
|
+
# that block will deserialize the metadata from that object and
|
19
|
+
# return the metadata. Returns the metadata, or nil if it could
|
20
|
+
# not be loaded.
|
21
|
+
def load(&block)
|
22
|
+
File.open(@path, "rb", &block)
|
23
|
+
rescue IOError, SystemCallError
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Rets
|
4
|
+
module Metadata
|
5
|
+
|
6
|
+
# Serialize/Deserialize metadata using JSON.
|
7
|
+
class JsonSerializer
|
8
|
+
|
9
|
+
# Serialize to a file. The library reserves the right to change
|
10
|
+
# the type or contents of o, so don't depend on it being
|
11
|
+
# anything in particular.
|
12
|
+
def save(file, o)
|
13
|
+
file.write o.to_json
|
14
|
+
end
|
15
|
+
|
16
|
+
# Deserialize from a file. If the metadata cannot be
|
17
|
+
# deserialized, return nil.
|
18
|
+
def load(file)
|
19
|
+
JSON.load(file)
|
20
|
+
rescue JSON::ParserError
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Rets
|
2
|
+
module Metadata
|
3
|
+
class LookupTable
|
4
|
+
attr_reader :resource_id, :lookup_types, :table_fragment, :name, :long_name
|
5
|
+
|
6
|
+
def initialize(resource_id, lookup_types, table_fragment)
|
7
|
+
@resource_id = resource_id
|
8
|
+
@lookup_types = lookup_types
|
9
|
+
|
10
|
+
@table_fragment = table_fragment
|
11
|
+
@name = table_fragment["SystemName"]
|
12
|
+
@long_name = table_fragment["LongName"]
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.build(table_fragment, resource_id, lookup_types)
|
16
|
+
lookup_name = table_fragment["LookupName"]
|
17
|
+
lookup_types = lookup_types[lookup_name]
|
18
|
+
new(resource_id, lookup_types, table_fragment)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Print the tree to a file
|
22
|
+
#
|
23
|
+
# [out] The file to print to. Defaults to $stdout.
|
24
|
+
def print_tree(out = $stdout)
|
25
|
+
out.puts "### LookupTable: #{name}"
|
26
|
+
out.puts " Resource: #{resource_id}"
|
27
|
+
out.puts " Required: #{table_fragment['Required']}"
|
28
|
+
out.puts " Searchable: #{ table_fragment["Searchable"] }"
|
29
|
+
out.puts " Units: #{ table_fragment["Units"] }"
|
30
|
+
out.puts " ShortName: #{ table_fragment["ShortName"] }"
|
31
|
+
out.puts " LongName: #{ long_name }"
|
32
|
+
out.puts " StandardName: #{ table_fragment["StandardName"] }"
|
33
|
+
out.puts "#### Types:"
|
34
|
+
|
35
|
+
lookup_types.each do |lookup_type|
|
36
|
+
lookup_type.print_tree(out)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def lookup_type(value)
|
41
|
+
lookup_types.detect {|lt| lt.value == value }
|
42
|
+
end
|
43
|
+
|
44
|
+
def resolve(value)
|
45
|
+
if value.empty?
|
46
|
+
return value.to_s.strip
|
47
|
+
end
|
48
|
+
|
49
|
+
#Remove surrounding quotes
|
50
|
+
clean_value = value.scan(/^["']?(.*?)["']?$/).join
|
51
|
+
|
52
|
+
|
53
|
+
lookup_type = lookup_type(clean_value)
|
54
|
+
|
55
|
+
resolved_value = lookup_type ? lookup_type.long_value : nil
|
56
|
+
|
57
|
+
if resolved_value.nil? && $VERBOSE
|
58
|
+
warn("Discarding unmappable value of #{clean_value.inspect}")
|
59
|
+
end
|
60
|
+
|
61
|
+
resolved_value.to_s.strip
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Rets
|
2
|
+
module Metadata
|
3
|
+
class LookupType
|
4
|
+
attr_reader :long_value, :value
|
5
|
+
|
6
|
+
def initialize(lookup_type_fragment)
|
7
|
+
@value = lookup_type_fragment["Value"].strip
|
8
|
+
@long_value = lookup_type_fragment["LongValue"].strip
|
9
|
+
end
|
10
|
+
|
11
|
+
# Print the tree to a file
|
12
|
+
#
|
13
|
+
# [out] The file to print to. Defaults to $stdout.
|
14
|
+
def print_tree(out = $stdout)
|
15
|
+
out.puts " #{long_value} -> #{value}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Rets
|
4
|
+
module Metadata
|
5
|
+
|
6
|
+
# Serialize/Deserialize metadata using Marshal.
|
7
|
+
class MarshalSerializer
|
8
|
+
|
9
|
+
# Serialize to a file. The library reserves the right to change
|
10
|
+
# the type or contents of o, so don't depend on it being
|
11
|
+
# anything in particular.
|
12
|
+
def save(file, o)
|
13
|
+
Marshal.dump(o, file)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Deserialize from a file. If the metadata cannot be
|
17
|
+
# deserialized, return nil.
|
18
|
+
def load(file)
|
19
|
+
Marshal.load(file)
|
20
|
+
rescue TypeError
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Rets
|
2
|
+
module Metadata
|
3
|
+
class MultiLookupTable
|
4
|
+
attr_reader :resource_id, :lookup_types, :table_fragment, :name, :long_name
|
5
|
+
|
6
|
+
def initialize(resource_id, lookup_types, table_fragment)
|
7
|
+
@resource_id = resource_id
|
8
|
+
@lookup_types = lookup_types
|
9
|
+
|
10
|
+
@table_fragment = table_fragment
|
11
|
+
@name = table_fragment["SystemName"]
|
12
|
+
@long_name = table_fragment["LongName"]
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.build(table_fragment, resource_id, lookup_types)
|
16
|
+
lookup_name = table_fragment["LookupName"]
|
17
|
+
lookup_types = lookup_types[lookup_name]
|
18
|
+
new(resource_id, lookup_types, table_fragment)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Print the tree to a file
|
22
|
+
#
|
23
|
+
# [out] The file to print to. Defaults to $stdout.
|
24
|
+
def print_tree(out = $stdout)
|
25
|
+
out.puts "### MultiLookupTable: #{name}"
|
26
|
+
out.puts " Resource: #{resource_id}"
|
27
|
+
out.puts " Required: #{table_fragment['Required']}"
|
28
|
+
out.puts " Searchable: #{ table_fragment["Searchable"] }"
|
29
|
+
out.puts " Units: #{ table_fragment["Units"] }"
|
30
|
+
out.puts " ShortName: #{ table_fragment["ShortName"] }"
|
31
|
+
out.puts " LongName: #{ long_name }"
|
32
|
+
out.puts " StandardName: #{ table_fragment["StandardName"] }"
|
33
|
+
out.puts " Types:"
|
34
|
+
|
35
|
+
lookup_types.each do |lookup_type|
|
36
|
+
lookup_type.print_tree(out)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def lookup_type(value)
|
41
|
+
lookup_types.detect {|lt| lt.value == value }
|
42
|
+
end
|
43
|
+
|
44
|
+
def resolve(value)
|
45
|
+
if value.empty?
|
46
|
+
return []
|
47
|
+
end
|
48
|
+
|
49
|
+
values = value.split(",")
|
50
|
+
|
51
|
+
values = values.map do |v|
|
52
|
+
|
53
|
+
#Remove surrounding quotes
|
54
|
+
clean_value = v.scan(/^["']?(.*?)["']?$/).join
|
55
|
+
|
56
|
+
|
57
|
+
lookup_type = lookup_type(clean_value)
|
58
|
+
|
59
|
+
resolved_value = lookup_type ? lookup_type.long_value : nil
|
60
|
+
|
61
|
+
if resolved_value.nil? && $VERBOSE
|
62
|
+
warn("Discarding unmappable value of #{clean_value.inspect}")
|
63
|
+
end
|
64
|
+
|
65
|
+
resolved_value.to_s.strip
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Rets
|
2
|
+
module Metadata
|
3
|
+
|
4
|
+
# This type of metadata cache, which is the default, neither saves
|
5
|
+
# nor restores.
|
6
|
+
class NullCache
|
7
|
+
|
8
|
+
# Save the metadata. Should yield an IO-like object to a block;
|
9
|
+
# that block will serialize the metadata to that object.
|
10
|
+
def save(&_block)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Load the metadata. Should yield an IO-like object to a block;
|
14
|
+
# that block will deserialize the metadata from that object and
|
15
|
+
# return the metadata. Returns the metadata, or nil if it could
|
16
|
+
# not be loaded.
|
17
|
+
def load(&_block)
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Rets
|
2
|
+
module Metadata
|
3
|
+
class Resource
|
4
|
+
class MissingRetsClass < RuntimeError; end
|
5
|
+
attr_reader :id, :key_field, :rets_classes, :rets_objects
|
6
|
+
|
7
|
+
def initialize(id, key_field, rets_classes, rets_objects)
|
8
|
+
@id = id
|
9
|
+
@key_field = key_field
|
10
|
+
@rets_classes = rets_classes
|
11
|
+
@rets_objects = rets_objects
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.find_lookup_containers(metadata, resource_id)
|
15
|
+
metadata[:lookup].select { |lc| lc.resource == resource_id }
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.find_lookup_type_containers(metadata, resource_id, lookup_name)
|
19
|
+
metadata[:lookup_type].select { |ltc| ltc.resource == resource_id && ltc.lookup == lookup_name }
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.find_rets_classes(metadata, resource_id)
|
23
|
+
class_container = metadata[:class].detect { |c| c.resource == resource_id }
|
24
|
+
if class_container.nil?
|
25
|
+
raise MissingRetsClass.new("No Metadata classes for #{resource_id}")
|
26
|
+
else
|
27
|
+
class_container.classes
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.find_rets_objects(metadata, resource_id)
|
32
|
+
objects = metadata[:object]
|
33
|
+
if objects
|
34
|
+
objects.select { |object| object.resource == resource_id }.map(&:objects).flatten
|
35
|
+
else
|
36
|
+
[]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.build_lookup_tree(resource_id, metadata)
|
41
|
+
lookup_types = Hash.new {|h, k| h[k] = Array.new }
|
42
|
+
|
43
|
+
find_lookup_containers(metadata, resource_id).each do |lookup_container|
|
44
|
+
lookup_container.lookups.each do |lookup_fragment|
|
45
|
+
lookup_name = lookup_fragment["LookupName"]
|
46
|
+
|
47
|
+
find_lookup_type_containers(metadata, resource_id, lookup_name).each do |lookup_type_container|
|
48
|
+
lookup_type_container.lookup_types.each do |lookup_type_fragment|
|
49
|
+
lookup_types[lookup_name] << LookupType.new(lookup_type_fragment)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
lookup_types
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.build_classes(resource_id, lookup_types, metadata)
|
59
|
+
find_rets_classes(metadata, resource_id).map do |rets_class_fragment|
|
60
|
+
RetsClass.build(rets_class_fragment, resource_id, lookup_types, metadata)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.build_objects(resource_id, metadata)
|
65
|
+
find_rets_objects(metadata, resource_id).map do |rets_object_fragment|
|
66
|
+
RetsObject.build(rets_object_fragment)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.build(resource_fragment, metadata, logger)
|
71
|
+
resource_id = resource_fragment["ResourceID"]
|
72
|
+
key_field = resource_fragment["KeyField"]
|
73
|
+
|
74
|
+
lookup_types = build_lookup_tree(resource_id, metadata)
|
75
|
+
rets_classes = build_classes(resource_id, lookup_types, metadata)
|
76
|
+
rets_objects = build_objects(resource_id, metadata)
|
77
|
+
|
78
|
+
new(resource_id, key_field, rets_classes, rets_objects)
|
79
|
+
rescue MissingRetsClass => e
|
80
|
+
logger.warn(e.message)
|
81
|
+
nil
|
82
|
+
end
|
83
|
+
|
84
|
+
# Print the tree to a file
|
85
|
+
#
|
86
|
+
# [out] The file to print to. Defaults to $stdout.
|
87
|
+
def print_tree(out = $stdout)
|
88
|
+
out.puts "# Resource: #{id} (Key Field: #{key_field})"
|
89
|
+
rets_classes.each do |rets_class|
|
90
|
+
rets_class.print_tree(out)
|
91
|
+
end
|
92
|
+
rets_objects.each do |rets_object|
|
93
|
+
rets_object.print_tree(out)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def find_rets_class(rets_class_name)
|
98
|
+
rets_classes.detect {|rc| rc.name == rets_class_name }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Rets
|
2
|
+
module Metadata
|
3
|
+
class RetsClass
|
4
|
+
attr_reader :name, :visible_name, :standard_name, :description, :tables
|
5
|
+
|
6
|
+
def initialize(name, visible_name, standard_name, description, tables)
|
7
|
+
@name = name
|
8
|
+
@visible_name = visible_name
|
9
|
+
@description = description
|
10
|
+
@standard_name = standard_name
|
11
|
+
|
12
|
+
@tables = tables
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.find_table_container(metadata, resource_id, class_name)
|
16
|
+
metadata[:table].detect { |t| t.resource == resource_id && t.class == class_name }
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.builds_tables(table_container, resource_id, lookup_types)
|
20
|
+
if table_container
|
21
|
+
table_container.tables.map do |table_fragment|
|
22
|
+
TableFactory.build(table_fragment, resource_id, lookup_types)
|
23
|
+
end
|
24
|
+
else
|
25
|
+
[]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.build(rets_class_fragment, resource_id, lookup_types, metadata)
|
30
|
+
class_name = rets_class_fragment["ClassName"]
|
31
|
+
visible_name = rets_class_fragment["VisibleName"]
|
32
|
+
standard_name = rets_class_fragment["StandardName"]
|
33
|
+
description = rets_class_fragment["Description"]
|
34
|
+
|
35
|
+
table_container = find_table_container(metadata, resource_id, class_name)
|
36
|
+
tables = builds_tables(table_container, resource_id, lookup_types)
|
37
|
+
new(class_name, visible_name, standard_name, description, tables)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Print the tree to a file
|
41
|
+
#
|
42
|
+
# [out] The file to print to. Defaults to $stdout.
|
43
|
+
def print_tree(out = $stdout)
|
44
|
+
out.puts "## Class: #{name}"
|
45
|
+
out.puts " Visible Name: #{visible_name}"
|
46
|
+
out.puts " Description : #{description}"
|
47
|
+
tables.each do |table|
|
48
|
+
table.print_tree(out)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def find_table(name)
|
53
|
+
tables.detect { |value| value.name.downcase == name.downcase }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Rets
|
2
|
+
module Metadata
|
3
|
+
class RetsObject
|
4
|
+
attr_reader :name, :mime_type, :description, :type
|
5
|
+
|
6
|
+
def initialize(type, name, mime_type, description)
|
7
|
+
@name = name
|
8
|
+
@mime_type = mime_type
|
9
|
+
@description = description
|
10
|
+
@type = type
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.build(rets_object_fragment)
|
14
|
+
rets_object_fragment = downcase_hash_keys(rets_object_fragment)
|
15
|
+
name = rets_object_fragment["visiblename"]
|
16
|
+
mime_type = rets_object_fragment["mimetype"]
|
17
|
+
description = rets_object_fragment["description"]
|
18
|
+
type = rets_object_fragment['objecttype']
|
19
|
+
new(type, name, mime_type, description)
|
20
|
+
end
|
21
|
+
|
22
|
+
def print_tree(out = $stdout)
|
23
|
+
out.puts " Object: #{type}"
|
24
|
+
out.puts " Visible Name: #{name}"
|
25
|
+
out.puts " Mime Type: #{mime_type}"
|
26
|
+
out.puts " Description: #{description}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def ==(other)
|
30
|
+
name == other.name &&
|
31
|
+
mime_type == other.mime_type &&
|
32
|
+
description == other.description
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def self.downcase_hash_keys(hash)
|
37
|
+
Hash[hash.map { |k, v| [k.downcase, v] }]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|