rets4r 0.8.5 → 1.1.18

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/.document +5 -0
  2. data/{test/client/data/1.5/metadata.xml → .gemtest} +0 -0
  3. data/CHANGELOG +611 -66
  4. data/CONTRIBUTORS +6 -2
  5. data/Gemfile +1 -0
  6. data/LICENSE +22 -0
  7. data/MANIFEST +63 -0
  8. data/NEWS +203 -0
  9. data/{README → README.rdoc} +11 -4
  10. data/RUBYS +7 -7
  11. data/Rakefile +48 -0
  12. data/TODO +5 -1
  13. data/examples/client_get_object.rb +31 -42
  14. data/examples/client_login.rb +20 -18
  15. data/examples/client_mapper.rb +17 -0
  16. data/examples/client_metadata.rb +28 -28
  17. data/examples/client_parser.rb +9 -0
  18. data/examples/client_search.rb +25 -27
  19. data/examples/settings.yml +114 -0
  20. data/lib/rets4r.rb +14 -1
  21. data/lib/rets4r/auth.rb +70 -66
  22. data/lib/rets4r/client.rb +470 -650
  23. data/lib/rets4r/client/data.rb +13 -13
  24. data/lib/rets4r/client/dataobject.rb +27 -19
  25. data/lib/rets4r/client/exceptions.rb +116 -0
  26. data/lib/rets4r/client/links.rb +32 -0
  27. data/lib/rets4r/client/metadata.rb +12 -12
  28. data/lib/rets4r/client/parsers/compact.rb +42 -0
  29. data/lib/rets4r/client/parsers/compact_nokogiri.rb +91 -0
  30. data/lib/rets4r/client/parsers/metadata.rb +92 -0
  31. data/lib/rets4r/client/parsers/response_parser.rb +100 -0
  32. data/lib/rets4r/client/requester.rb +143 -0
  33. data/lib/rets4r/client/transaction.rb +30 -33
  34. data/lib/rets4r/core_ext/array/extract_options.rb +15 -0
  35. data/lib/rets4r/core_ext/class/attribute_accessors.rb +58 -0
  36. data/lib/rets4r/core_ext/hash/keys.rb +46 -0
  37. data/lib/rets4r/core_ext/hash/slice.rb +39 -0
  38. data/lib/rets4r/listing_mapper.rb +17 -0
  39. data/lib/rets4r/listing_service.rb +35 -0
  40. data/lib/rets4r/loader.rb +8 -0
  41. data/lib/tasks/annotations.rake +121 -0
  42. data/lib/tasks/coverage.rake +13 -0
  43. data/rets4r.gemspec +24 -0
  44. data/spec/rets4r_compact_data_parser_spec.rb +7 -0
  45. data/test/data/1.5/bad_compact.xml +7 -0
  46. data/test/data/1.5/count_only_compact.xml +3 -0
  47. data/test/{client/data → data}/1.5/error.xml +0 -0
  48. data/test/{client/data → data}/1.5/invalid_compact.xml +0 -0
  49. data/test/{client/data → data}/1.5/login.xml +0 -0
  50. data/test/data/1.5/metadata.xml +0 -0
  51. data/test/{client/data → data}/1.5/search_compact.xml +0 -0
  52. data/test/data/1.5/search_compact_big.xml +136 -0
  53. data/test/{client/data → data}/1.5/search_unescaped_compact.xml +0 -0
  54. data/test/data/listing_service.yml +36 -0
  55. data/test/test_auth.rb +68 -0
  56. data/test/test_client.rb +342 -0
  57. data/test/test_client_links.rb +39 -0
  58. data/test/test_compact_nokogiri.rb +64 -0
  59. data/test/test_helper.rb +12 -0
  60. data/test/test_listing_mapper.rb +112 -0
  61. data/test/test_loader.rb +24 -0
  62. data/test/test_parser.rb +96 -0
  63. data/test/test_quality.rb +57 -0
  64. metadata +168 -53
  65. data/GPL +0 -340
  66. data/examples/metadata.xml +0 -42
  67. data/lib/rets4r/client/metadataindex.rb +0 -82
  68. data/lib/rets4r/client/parser.rb +0 -141
  69. data/lib/rets4r/client/parser/rexml.rb +0 -75
  70. data/lib/rets4r/client/parser/xmlparser.rb +0 -95
  71. data/test/client/parser/tc_rexml.rb +0 -17
  72. data/test/client/parser/tc_xmlparser.rb +0 -21
  73. data/test/client/tc_auth.rb +0 -68
  74. data/test/client/tc_client.rb +0 -320
  75. data/test/client/tc_metadataindex.rb +0 -36
  76. data/test/client/test_parser.rb +0 -128
  77. data/test/client/ts_all.rb +0 -8
  78. data/test/ts_all.rb +0 -1
  79. data/test/ts_client.rb +0 -1
@@ -0,0 +1,17 @@
1
+ module RETS4R
2
+ class ListingMapper
3
+ def initialize(params = {})
4
+ @select = params[:select] || ListingService.connection[:select]
5
+ end
6
+ def select
7
+ @select
8
+ end
9
+ def map(original)
10
+ listing = {}
11
+ @select.each_pair {|rets_key, record_key|
12
+ listing[record_key] = original[rets_key]
13
+ }
14
+ listing
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,35 @@
1
+ require 'rets4r/core_ext/class/attribute_accessors'
2
+ require 'rets4r/core_ext/hash/keys'
3
+ require 'rets4r/core_ext/hash/slice'
4
+
5
+ module RETS4R
6
+ class ListingService
7
+ # RECORD_COUNT_ONLY=Librets::SearchRequest::RECORD_COUNT_ONLY
8
+ RECORD_COUNT_ONLY='fixme'
9
+ # Contains the listing service configurations - typically stored in
10
+ # config/listing_service.yml - as a Hash.
11
+ cattr_accessor :configurations, :instance_writer => false
12
+ cattr_accessor :env, :instance_writer => false
13
+
14
+ class << self
15
+
16
+ # Connection configuration for the specified environment, or the current
17
+ # environment if none is given.
18
+ def connection(spec = nil)
19
+ case spec
20
+ when nil
21
+ connection(RETS4R::ListingService.env)
22
+ when Symbol, String
23
+ if configuration = configurations[spec.to_s]
24
+ configuration.symbolize_keys
25
+ else
26
+ raise ArgumentError, "#{spec} listing service is not configured"
27
+ end
28
+ else
29
+ raise ArgumentError, "#{spec} listing service is not configured"
30
+ end
31
+ end
32
+
33
+ end # class << self
34
+ end
35
+ end
@@ -0,0 +1,8 @@
1
+ module RETS4R
2
+ class Loader
3
+ def self.load(io, &block)
4
+ parser = RETS4R::Client::CompactNokogiriParser.new(io)
5
+ parser.each(&block)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,121 @@
1
+ # Implements the logic behind the rake tasks for annotations like
2
+ #
3
+ # rake notes
4
+ # rake notes:optimize
5
+ #
6
+ # and friends. See <tt>rake -T notes</tt> and <tt>railties/lib/tasks/annotations.rake</tt>.
7
+ #
8
+ # Annotation objects are triplets <tt>:line</tt>, <tt>:tag</tt>, <tt>:text</tt> that
9
+ # represent the line where the annotation lives, its tag, and its text. Note
10
+ # the filename is not stored.
11
+ #
12
+ # Annotations are looked for in comments and modulus whitespace they have to
13
+ # start with the tag optionally followed by a colon. Everything up to the end
14
+ # of the line (or closing ERb comment tag) is considered to be their text.
15
+ class SourceAnnotationExtractor
16
+ class Annotation < Struct.new(:line, :tag, :text)
17
+
18
+ # Returns a representation of the annotation that looks like this:
19
+ #
20
+ # [126] [TODO] This algorithm is simple and clearly correct, make it faster.
21
+ #
22
+ # If +options+ has a flag <tt>:tag</tt> the tag is shown as in the example above.
23
+ # Otherwise the string contains just line and text.
24
+ def to_s(options={})
25
+ s = "[%3d] " % line
26
+ s << "[#{tag}] " if options[:tag]
27
+ s << text
28
+ end
29
+ end
30
+
31
+ # Prints all annotations with tag +tag+ under the root directories +app+, +lib+,
32
+ # and +test+ (recursively). Only filenames with extension +.builder+, +.rb+,
33
+ # +.rxml+, +.rjs+, +.rhtml+, or +.erb+ are taken into account. The +options+
34
+ # hash is passed to each annotation's +to_s+.
35
+ #
36
+ # This class method is the single entry point for the rake tasks.
37
+ def self.enumerate(tag, options={})
38
+ extractor = new(tag)
39
+ extractor.display(extractor.find, options)
40
+ end
41
+
42
+ attr_reader :tag
43
+
44
+ def initialize(tag)
45
+ @tag = tag
46
+ end
47
+
48
+ # Returns a hash that maps filenames under +dirs+ (recursively) to arrays
49
+ # with their annotations. Only files with annotations are included, and only
50
+ # those with extension +.builder+, +.rb+, +.rxml+, +.rjs+, +.rhtml+, and +.erb+
51
+ # are taken into account.
52
+ def find(dirs=%w(app lib test))
53
+ dirs.inject({}) { |h, dir| h.update(find_in(dir)) }
54
+ end
55
+
56
+ # Returns a hash that maps filenames under +dir+ (recursively) to arrays
57
+ # with their annotations. Only files with annotations are included, and only
58
+ # those with extension +.builder+, +.rb+, +.rxml+, +.rjs+, +.rhtml+, and +.erb+
59
+ # are taken into account.
60
+ def find_in(dir)
61
+ results = {}
62
+
63
+ Dir.glob("#{dir}/*") do |item|
64
+ next if File.basename(item)[0] == ?.
65
+
66
+ if File.directory?(item)
67
+ results.update(find_in(item))
68
+ elsif item =~ /\.(builder|(r(?:b|xml|js)))$/
69
+ results.update(extract_annotations_from(item, /#\s*(#{tag}):?\s*(.*)$/))
70
+ elsif item =~ /\.(rhtml|erb)$/
71
+ results.update(extract_annotations_from(item, /<%\s*#\s*(#{tag}):?\s*(.*?)\s*%>/))
72
+ end
73
+ end
74
+
75
+ results
76
+ end
77
+
78
+ # If +file+ is the filename of a file that contains annotations this method returns
79
+ # a hash with a single entry that maps +file+ to an array of its annotations.
80
+ # Otherwise it returns an empty hash.
81
+ def extract_annotations_from(file, pattern)
82
+ lineno = 0
83
+ result = File.readlines(file).inject([]) do |list, line|
84
+ lineno += 1
85
+ next list unless line =~ pattern
86
+ list << Annotation.new(lineno, $1, $2)
87
+ end
88
+ result.empty? ? {} : { file => result }
89
+ end
90
+
91
+ # Prints the mapping from filenames to annotations in +results+ ordered by filename.
92
+ # The +options+ hash is passed to each annotation's +to_s+.
93
+ def display(results, options={})
94
+ results.keys.sort.each do |file|
95
+ puts "#{file}:"
96
+ results[file].each do |note|
97
+ puts " * #{note.to_s(options)}"
98
+ end
99
+ puts
100
+ end
101
+ end
102
+ end
103
+
104
+ desc "Enumerate all annotations"
105
+ task :notes do
106
+ SourceAnnotationExtractor.enumerate "OPTIMIZE|FIXME|TODO", :tag => true
107
+ end
108
+
109
+ namespace :notes do
110
+ ["OPTIMIZE", "FIXME", "TODO"].each do |annotation|
111
+ desc "Enumerate all #{annotation} annotations"
112
+ task annotation.downcase.intern do
113
+ SourceAnnotationExtractor.enumerate annotation
114
+ end
115
+ end
116
+
117
+ desc "Enumerate a custom annotation, specify with ANNOTATION=WTFHAX"
118
+ task :custom do
119
+ SourceAnnotationExtractor.enumerate ENV['ANNOTATION']
120
+ end
121
+ end
@@ -0,0 +1,13 @@
1
+ begin
2
+ require 'rcov/rcovtask'
3
+ Rcov::RcovTask.new do |test|
4
+ test.libs << 'test'
5
+ test.pattern = "test/test_*.rb"
6
+ test.verbose = true
7
+ test.rcov_opts << ' --sort coverage'
8
+ end
9
+ rescue LoadError
10
+ task :rcov do
11
+ abort "RCov is not available. In order to run rcov, you must: gem install rcov"
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ lib = File.expand_path("../lib/rets4r.rb", __FILE__)
2
+ version = File.read(lib)[/^\s*VERSION\s*=\s*(['"])(\d\.\d\.\d+)\1/, 2]
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = 'rets4r'
6
+ spec.authors = ["Scott Patterson", "John Wulff", "bgetting", "Jacob Basham", "Joseph Holsten"]
7
+ spec.email = ["scott.patterson@digitalaun.com", "john@johnwulff.com", "brian@terra-firma-design.com", "jacob@paperpigeons.net", "joseph@josephholsten.com"]
8
+ spec.homepage = 'http://rets4r.rubyforge.org/'
9
+ spec.rubyforge_project = 'rets4r'
10
+ spec.description = %q{RETS4R is a native Ruby interface to the RETS (Real Estate Transaction Standard). It currently is built for the 1.5 specification, but support for 1.7 and 2.0 are planned. It does not currently implement all of the specification, but the most commonly used portions. Specifically, there is no support for Update transactions.}
11
+ spec.extra_rdoc_files = %w[CHANGELOG CONTRIBUTORS LICENSE MANIFEST NEWS README.rdoc RUBYS TODO ]
12
+ spec.rdoc_options << "--charset=UTF-8" <<
13
+ "--main" << "README.rdoc"
14
+ spec.version = version
15
+ spec.summary = spec.description.split(/\.\s+/).first
16
+ spec.files = File.read("MANIFEST").split(/\r?\n\r?/)
17
+
18
+ spec.add_runtime_dependency 'nokogiri', '~>1.4.3.0'
19
+ spec.add_development_dependency 'mocha'
20
+ spec.add_development_dependency 'shoulda'
21
+ spec.add_development_dependency 'activesupport'
22
+ spec.add_development_dependency 'rake'
23
+ end
24
+
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+ require 'rets4r/client/parsers/compact'
3
+
4
+ describe RETS4R::Client::CompactDataParser do
5
+ it "should parse results without a delimiter in response"
6
+ it "should parse data without column names in response"
7
+ end
@@ -0,0 +1,7 @@
1
+ <RETS ReplyCode="0" ReplyText="Operation Successful">
2
+ <COUNT Records="38610" />
3
+ <DELIMITER value="09" />
4
+ <COLUMNS> MLSNUM AGENTLIST AGENTLIST_FULLNAME OFFICELIST OFFICELIST_OFFICENAM1 INTERNETLIST_ALL LISTSTATUSFLAG PHOTOCOUNT LISTSTATUS BEDS CARPORTCAP NUMDININGAREAS FIREPLACES BATHSFULL BATHSFULLLEVEL1 BATHSFULLLEVEL2 BATHSFULLLEVEL3 BATHSFULLBASEMENT GARAGECAP BATHSHALF BATHSHALFLEVEL1 BATHSHALFLEVEL2 BATHSHALFLEVEL3 BATHSHALFBASEMENT NUMLIVINGAREAS NUMBARNS NUMLAKES NUMPONDS NUMRESIDENCE NUMSTOCKTANKS STORIES NUMWELLS SCHOOLDISTRICT AGENTSELL_FULLNAME OFFICESELL_OFFICENAM2 STATE STREETNAME STREETTYPE STREETNUM STREETNUMDISPLAY STREETDIRSUFFIX STREETDIR STYLE SUBDIVISION ACRES INTERNETDISPLAYYN ROOMBEDBATHDESC STORIESBLDG CITY COMMONFEATURES COUNTY ACRESCULTIVATED INTERIOR LISTPRICE SQFTPRICE LOTNUM LOTDESC LOTDIM LOTSIZE LISTPRICELOW ACRESPRICE MAPBOOK MAPCOORD MAPPAGE LISTPRICEORIG GARAGEDESC PROPTYPE REMARKS PROPSUBTYPE PROPSUBTYPEDISPLAY SQFTTOTAL SQFTSOURCE UTILITIES UID UIDPRP TAXUNEXEMPT UNITNUM YEARBUILT ZIPCODE </COLUMNS>
5
+ <DATA> 11322886 000533246 CD Boyd, II FMAB McClure, REALTORS OMWS,immo,RCOM,SYND 8 act 4 0 2 1 3 2 0 2 2 Wylie ISD, Taylor Co. TX Thompson PKWY 8317 8317 STYRANCH Belle Vista Estates 0.361 Y BBFJET-TUB,BBFLIN-CLO,BBFSEP-SHO,BBFW+I-CLO Abilene Taylor INFBAY-WIN,INFCABLE-A,INFDECLTNG,INFHIGHSPD,INFLOFT,INFSOUND-W,INFVAUL-CL,INFWIND-CO 239000 84.6 LTDSUBDIV 87X182 LTS.5-.99A 0 662049 OT none 9999 239000 PARATTACHE,PAROPENER,PARREAR,PARSIDE RES Custom home built in 05&apos;. Growing neighborhood convienient to Wylie schools. Abundant living areas and and open space floor plan.Could be 5th BDRM, or upstair BDRM &amp; Bath could be another living area. Perfect office next to Master. Gourmet sized kitchen, granite countertops, barell ceilings, wonderful storage space!!!! S RES-S 2825 TAX STUASPHALT,UTLCITY-SE,UTLCITY-WA,STUCONCRET 3211078 3211078 5876 2005 79606-6652 </DATA>
6
+ <MAXROWS />
7
+ </RETS>
@@ -0,0 +1,3 @@
1
+ <RETS ReplyCode="0" ReplyText="SUCCESS">
2
+ <COUNT Records="1024"/>
3
+ </RETS>
File without changes
File without changes
File without changes
@@ -0,0 +1,136 @@
1
+ <RETS ReplyCode="0" ReplyText="SUCCESS">
2
+ <COUNT Records="4"/>
3
+ <DELIMITER value="09" />
4
+ <COLUMNS> First Second Third </COLUMNS>
5
+ <DATA> Datum1 Datum2 Datum3</DATA>
6
+ <DATA> Datum4 Datum5 Datum6</DATA>
7
+ <DATA> Datum4 Datum5 Datum6</DATA>
8
+ <DATA> Datum1 Datum2 Datum3</DATA>
9
+ <DATA> Datum1 Datum2 Datum3</DATA>
10
+ <DATA> Datum4 Datum5 Datum6</DATA>
11
+ <DATA> Datum4 Datum5 Datum6</DATA>
12
+ <DATA> Datum1 Datum2 Datum3</DATA>
13
+ <DATA> Datum4 Datum5 Datum6</DATA>
14
+ <DATA> Datum4 Datum5 Datum6</DATA>
15
+ <DATA> Datum1 Datum2 Datum3</DATA>
16
+ <DATA> Datum1 Datum2 Datum3</DATA>
17
+ <DATA> Datum4 Datum5 Datum6</DATA>
18
+ <DATA> Datum4 Datum5 Datum6</DATA>
19
+ <DATA> Datum1 Datum2 Datum3</DATA>
20
+ <DATA> Datum4 Datum5 Datum6</DATA>
21
+ <DATA> Datum4 Datum5 Datum6</DATA>
22
+ <DATA> Datum1 Datum2 Datum3</DATA>
23
+ <DATA> Datum1 Datum2 Datum3</DATA>
24
+ <DATA> Datum4 Datum5 Datum6</DATA>
25
+ <DATA> Datum4 Datum5 Datum6</DATA>
26
+ <DATA> Datum1 Datum2 Datum3</DATA>
27
+ <DATA> Datum4 Datum5 Datum6</DATA>
28
+ <DATA> Datum4 Datum5 Datum6</DATA>
29
+ <DATA> Datum1 Datum2 Datum3</DATA>
30
+ <DATA> Datum1 Datum2 Datum3</DATA>
31
+ <DATA> Datum4 Datum5 Datum6</DATA>
32
+ <DATA> Datum4 Datum5 Datum6</DATA>
33
+ <DATA> Datum1 Datum2 Datum3</DATA>
34
+ <DATA> Datum4 Datum5 Datum6</DATA>
35
+ <DATA> Datum4 Datum5 Datum6</DATA>
36
+ <DATA> Datum1 Datum2 Datum3</DATA>
37
+ <DATA> Datum1 Datum2 Datum3</DATA>
38
+ <DATA> Datum4 Datum5 Datum6</DATA>
39
+ <DATA> Datum4 Datum5 Datum6</DATA>
40
+ <DATA> Datum1 Datum2 Datum3</DATA>
41
+ <DATA> Datum4 Datum5 Datum6</DATA>
42
+ <DATA> Datum4 Datum5 Datum6</DATA>
43
+ <DATA> Datum1 Datum2 Datum3</DATA>
44
+ <DATA> Datum1 Datum2 Datum3</DATA>
45
+ <DATA> Datum4 Datum5 Datum6</DATA>
46
+ <DATA> Datum4 Datum5 Datum6</DATA>
47
+ <DATA> Datum1 Datum2 Datum3</DATA>
48
+ <DATA> Datum4 Datum5 Datum6</DATA>
49
+ <DATA> Datum4 Datum5 Datum6</DATA>
50
+ <DATA> Datum1 Datum2 Datum3</DATA>
51
+ <DATA> Datum1 Datum2 Datum3</DATA>
52
+ <DATA> Datum4 Datum5 Datum6</DATA>
53
+ <DATA> Datum4 Datum5 Datum6</DATA>
54
+ <DATA> Datum1 Datum2 Datum3</DATA>
55
+ <DATA> Datum4 Datum5 Datum6</DATA>
56
+ <DATA> Datum4 Datum5 Datum6</DATA>
57
+ <DATA> Datum1 Datum2 Datum3</DATA>
58
+ <DATA> Datum1 Datum2 Datum3</DATA>
59
+ <DATA> Datum4 Datum5 Datum6</DATA>
60
+ <DATA> Datum4 Datum5 Datum6</DATA>
61
+ <DATA> Datum1 Datum2 Datum3</DATA>
62
+ <DATA> Datum4 Datum5 Datum6</DATA>
63
+ <DATA> Datum4 Datum5 Datum6</DATA>
64
+ <DATA> Datum1 Datum2 Datum3</DATA>
65
+ <DATA> Datum1 Datum2 Datum3</DATA>
66
+ <DATA> Datum4 Datum5 Datum6</DATA>
67
+ <DATA> Datum4 Datum5 Datum6</DATA>
68
+ <DATA> Datum1 Datum2 Datum3</DATA>
69
+ <DATA> Datum4 Datum5 Datum6</DATA>
70
+ <DATA> Datum4 Datum5 Datum6</DATA>
71
+ <DATA> Datum1 Datum2 Datum3</DATA>
72
+ <DATA> Datum1 Datum2 Datum3</DATA>
73
+ <DATA> Datum4 Datum5 Datum6</DATA>
74
+ <DATA> Datum4 Datum5 Datum6</DATA>
75
+ <DATA> Datum1 Datum2 Datum3</DATA>
76
+ <DATA> Datum4 Datum5 Datum6</DATA>
77
+ <DATA> Datum4 Datum5 Datum6</DATA>
78
+ <DATA> Datum1 Datum2 Datum3</DATA>
79
+ <DATA> Datum1 Datum2 Datum3</DATA>
80
+ <DATA> Datum4 Datum5 Datum6</DATA>
81
+ <DATA> Datum4 Datum5 Datum6</DATA>
82
+ <DATA> Datum1 Datum2 Datum3</DATA>
83
+ <DATA> Datum4 Datum5 Datum6</DATA>
84
+ <DATA> Datum4 Datum5 Datum6</DATA>
85
+ <DATA> Datum1 Datum2 Datum3</DATA>
86
+ <DATA> Datum1 Datum2 Datum3</DATA>
87
+ <DATA> Datum4 Datum5 Datum6</DATA>
88
+ <DATA> Datum4 Datum5 Datum6</DATA>
89
+ <DATA> Datum1 Datum2 Datum3</DATA>
90
+ <DATA> Datum4 Datum5 Datum6</DATA>
91
+ <DATA> Datum4 Datum5 Datum6</DATA>
92
+ <DATA> Datum1 Datum2 Datum3</DATA>
93
+ <DATA> Datum1 Datum2 Datum3</DATA>
94
+ <DATA> Datum4 Datum5 Datum6</DATA>
95
+ <DATA> Datum4 Datum5 Datum6</DATA>
96
+ <DATA> Datum1 Datum2 Datum3</DATA>
97
+ <DATA> Datum4 Datum5 Datum6</DATA>
98
+ <DATA> Datum4 Datum5 Datum6</DATA>
99
+ <DATA> Datum1 Datum2 Datum3</DATA>
100
+ <DATA> Datum1 Datum2 Datum3</DATA>
101
+ <DATA> Datum4 Datum5 Datum6</DATA>
102
+ <DATA> Datum4 Datum5 Datum6</DATA>
103
+ <DATA> Datum1 Datum2 Datum3</DATA>
104
+ <DATA> Datum4 Datum5 Datum6</DATA>
105
+ <DATA> Datum4 Datum5 Datum6</DATA>
106
+ <DATA> Datum1 Datum2 Datum3</DATA>
107
+ <DATA> Datum1 Datum2 Datum3</DATA>
108
+ <DATA> Datum4 Datum5 Datum6</DATA>
109
+ <DATA> Datum4 Datum5 Datum6</DATA>
110
+ <DATA> Datum1 Datum2 Datum3</DATA>
111
+ <DATA> Datum4 Datum5 Datum6</DATA>
112
+ <DATA> Datum4 Datum5 Datum6</DATA>
113
+ <DATA> Datum1 Datum2 Datum3</DATA>
114
+ <DATA> Datum1 Datum2 Datum3</DATA>
115
+ <DATA> Datum4 Datum5 Datum6</DATA>
116
+ <DATA> Datum4 Datum5 Datum6</DATA>
117
+ <DATA> Datum1 Datum2 Datum3</DATA>
118
+ <DATA> Datum4 Datum5 Datum6</DATA>
119
+ <DATA> Datum4 Datum5 Datum6</DATA>
120
+ <DATA> Datum1 Datum2 Datum3</DATA>
121
+ <DATA> Datum1 Datum2 Datum3</DATA>
122
+ <DATA> Datum4 Datum5 Datum6</DATA>
123
+ <DATA> Datum4 Datum5 Datum6</DATA>
124
+ <DATA> Datum1 Datum2 Datum3</DATA>
125
+ <DATA> Datum4 Datum5 Datum6</DATA>
126
+ <DATA> Datum4 Datum5 Datum6</DATA>
127
+ <DATA> Datum1 Datum2 Datum3</DATA>
128
+ <DATA> Datum1 Datum2 Datum3</DATA>
129
+ <DATA> Datum1 Datum2 Datum3</DATA>
130
+ <DATA> Datum4 Datum5 Datum6</DATA>
131
+ <DATA> Datum4 Datum5 Datum6</DATA>
132
+ <DATA> Datum1 Datum2 Datum3</DATA>
133
+ <DATA> Datum4 Datum5 Datum6</DATA>
134
+ <DATA> Datum4 Datum5 Datum6</DATA>
135
+ <MAXROWS/>
136
+ </RETS>
@@ -0,0 +1,36 @@
1
+ # This file specifies the access information for the listing service #
2
+ # To access the standard RETS demo server, use:
3
+ #
4
+ test:
5
+ url: http://demo.crt.realtors.org:6103/rets/login
6
+ username: Joe
7
+ password: Schmoe
8
+ version: 1.7.2
9
+ resource: Property
10
+ class: RES
11
+ object_type: Photo
12
+ select:
13
+ ListingID: :mls
14
+ AgentID: :agent_id
15
+ ModificationTimestamp: :rets_update_at
16
+ Status: :status
17
+ # ListDate: :list_date
18
+ ListPrice: :list_price
19
+ # ClosePrice: :close_price
20
+ # ContractDate: :contract_date
21
+ StreetNumber: :street_number
22
+ StreetName: :street_name
23
+ StreetDirection: :street_direction
24
+ Unit: :unit_number
25
+ ZipCode: :zip_code
26
+ City: :city
27
+ County: :county
28
+ # Rooms: :rooms
29
+ # State: :state
30
+ # Board: :board
31
+ LivingArea: :living_area
32
+ Baths: :baths
33
+ Bedrooms: :beds
34
+ Garage: :garage
35
+ SqFt: :square_feet
36
+ YearBuilt: :year_built
@@ -0,0 +1,68 @@
1
+ $:.unshift File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
2
+
3
+ require 'rets4r/auth'
4
+ require 'test/unit'
5
+
6
+ module RETS4R
7
+ class TestAuth < Test::Unit::TestCase
8
+ def setup
9
+ @useragent = 'TestAgent/0.00'
10
+ @username = 'username'
11
+ @password = 'password'
12
+ @realm = 'REALM'
13
+ @nonce = '2006-03-03T17:37:10'
14
+ end
15
+
16
+ def test_authenticate
17
+ response = {
18
+ 'www-authenticate' => 'Digest qop="auth",realm="'+ @realm +'",nonce="'+ @nonce +'",opaque="",stale="false",domain="\my\test\domain"'
19
+ }
20
+
21
+ Auth.authenticate(response, @username, @password, '/my/rets/url', 'GET', Auth.request_id, @useragent)
22
+ end
23
+
24
+ # test without spacing
25
+ def test_parse_auth_header_without_spacing
26
+ header = 'Digest qop="auth",realm="'+ @realm +'",nonce="'+ @nonce +'",opaque="",stale="false",domain="\my\test\domain"'
27
+ check_header(header)
28
+ end
29
+
30
+ # test with spacing between each item
31
+ def test_parse_auth_header_with_spacing
32
+ header = 'Digest qop="auth", realm="'+ @realm +'", nonce="'+ @nonce +'", opaque="", stale="false", domain="\my\test\domain"'
33
+ check_header(header)
34
+ end
35
+
36
+ # used to check the that the header was processed properly.
37
+ def check_header(header)
38
+ results = Auth.parse_header(header)
39
+
40
+ assert_equal('auth', results['qop'])
41
+ assert_equal('REALM', results['realm'])
42
+ assert_equal('2006-03-03T17:37:10', results['nonce'])
43
+ assert_equal('', results['opaque'])
44
+ assert_equal('false', results['stale'])
45
+ assert_equal('\my\test\domain', results['domain'])
46
+ end
47
+
48
+ def test_calculate_digest
49
+ # with qop
50
+ assert_equal('c5f9ef280f0ca78ed7a488158fc2f4cc', Auth.calculate_digest(@username, \
51
+ @password, @realm, 'test', 'GET', '/my/rets/url', true, 'test'))
52
+
53
+ # without qop
54
+ assert_equal('bceafa34467a3519c2f6295d4800f4ea', Auth.calculate_digest(@username, \
55
+ @password, @realm, 'test', 'GET', '/my/rets/url', false))
56
+ end
57
+
58
+ def test_request_id
59
+ assert_not_nil(true, Auth.request_id)
60
+ end
61
+
62
+ def test_cnonce
63
+ # We call cnonce with a static request ID so that we have a consistent result with which
64
+ # to test against
65
+ assert_equal('d5cdfa1acffde590d263689fb40cf53c', Auth.cnonce(@useragent, @password, 'requestId', @nonce))
66
+ end
67
+ end
68
+ end