tourets 0.1.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.
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .rvmrc
7
+ .rspec
8
+ Gemfile.lock
9
+ InstalledFiles
10
+ _yardoc
11
+ coverage
12
+ doc/
13
+ lib/bundler/man
14
+ pkg
15
+ rdoc
16
+ spec/reports
17
+ test/tmp
18
+ test/version_tmp
19
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in tourets.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Jeremy Woertink & Justin Mitchell
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,97 @@
1
+ # TouRETS
2
+
3
+ The name came from a play on Tourette's and RETS. Because when you work with RETS you feel like you have Tourette's.
4
+ The goal of this gem is to make a really clean and familiar API for connecting to a RETS server. This gem was designed to be used with rails, so it takes a lot of the API that rails uses.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'tourets', :github => 'jwoertink/tourets'
11
+
12
+ Add a rets_config.yml to the config directory of your rails app
13
+
14
+ ```yaml
15
+ -
16
+ mls: 'GLVAR'
17
+ url: 'http://las.rets.interealty.com/Login.asmx/Login'
18
+ username: 'user'
19
+ password: 'secret'
20
+ rets_version: '1.8'
21
+ auth_mode: :digest
22
+ useragent:
23
+ name: 'some_user_connector'
24
+ ```
25
+
26
+ And then execute:
27
+
28
+ $ bundle
29
+
30
+ Or install it yourself as:
31
+
32
+ $ gem install tourets
33
+
34
+ ## Usage
35
+ (since this is still new, this usage may change often)
36
+
37
+ ```ruby
38
+ begin
39
+ TouRETS.establish_connection('GLVAR')
40
+ # Get a list of properties with 3 bedrooms and 2 bathrooms
41
+ @properties = TouRETS::Property.where(:bedrooms => 3, :bathrooms => 2)
42
+ # Grab all the photos for the first property
43
+ @photos = @properties.first.photos
44
+ # Iterate through all those photos, save them into tempfiles, and then do stuff?
45
+ @photos.each do |photo|
46
+ begin
47
+ tmp = Tempfile.new(photo.filename)
48
+ tmp.write(photo.image)
49
+ # you have an actual photo now. Maybe push to S3?
50
+ ensure
51
+ tmp.close
52
+ tmp.unlink
53
+ end
54
+ end
55
+ ensure
56
+ TouRETS.close_connection
57
+ end
58
+ ```
59
+
60
+ Before doing anything, you must make your first initial connection to your MLS server.
61
+ ```ruby
62
+ if TouRETS.current_connection?
63
+ # Get the sysid for the last property in that instance array
64
+ property_id = @properties.last.id
65
+ puts property_id #=> 1234567
66
+ # Grab the second picture for that property
67
+ picture = TouRETS::Photo.find(property_id, :id => 2)
68
+ puts picture.content_type #=> image/jpeg
69
+ puts picture.filename #=> 1234567-2.jpg
70
+ puts picture.id #=> 2
71
+ # This returns the binary string data that gets written to a file
72
+ puts picture.image.class #=> String
73
+ end
74
+ ```
75
+
76
+ ## ruby-rets
77
+ This gem relies heavily on the work done by Zachary Anker (zanker) with the [ruby-rets](https://github.com/Placester/ruby-rets) gem.
78
+ There is a `TouRETS::Search#find` class which just makes a direct call to the `RETS::Base::Core#search`. This gives you the option to still use
79
+ that gem inside of this one.
80
+
81
+ ## TODO (in no particular order)
82
+ 1. Add in other Objects like VRImage, Thumbnail, Map, Video, Audio
83
+ 2. Add in other Resources like Agent, Office, LotsAndLand
84
+ 3. CLEAN CODE!!!!!
85
+ 4. Add in limits, and selects to searches
86
+ 5. TEST TEST TEST TEST
87
+ 6. Add some passing specs.
88
+ 7. Document the hell out of this thing
89
+
90
+
91
+ ## Contributing
92
+
93
+ 1. Fork it
94
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
95
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
96
+ 4. Push to the branch (`git push origin my-new-feature`)
97
+ 5. Create new Pull Request
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,61 @@
1
+ require 'ruby-rets'
2
+ require 'tourets/rails'
3
+ require 'tourets/utilities'
4
+ require 'tourets/exceptions'
5
+ require 'tourets/extensions/hash' # This should go away after Rails 4.0
6
+ require 'tourets/search'
7
+ require 'tourets/photo'
8
+ require 'tourets/property'
9
+
10
+ module TouRETS
11
+ include Utilities
12
+
13
+ class << self
14
+ attr_accessor :current_connection
15
+
16
+ # This will take the specific MLS you want to connect to
17
+ # There must be a rets_config.yml file in the :rails_root/config directory
18
+ #
19
+ def establish_connection(mls)
20
+ config_file = File.join(app_root, 'config', 'rets_config.yml')
21
+ if File.exists?(config_file)
22
+ connections = YAML.load_file(config_file)
23
+ conn = connections.select { |conn| conn['mls'] == mls }.first
24
+ if conn.nil?
25
+ raise ConfigurationError, "Missing or incorrect MLS key `#{mls}`."
26
+ else
27
+ conn.recursive_symbolize_keys!
28
+ conn.delete(:mls)
29
+ self.current_connection = RETS::Client.login(conn)
30
+ end
31
+ else
32
+ raise ConfigurationError, "Couldn't find Config File in #{config_file}."
33
+ end
34
+ end
35
+
36
+ # Closes the current connection to the RETS server
37
+ def close_connection
38
+ current_connection.logout if current_connection?
39
+ current_connection = nil
40
+ end
41
+
42
+ def ensure_connected!
43
+ raise ConnectionError, "You must establish a connection first." unless current_connection?
44
+ end
45
+
46
+ def current_connection?
47
+ !current_connection.nil?
48
+ end
49
+
50
+ # The root of the application using this gem
51
+ def app_root
52
+ if defined?(Rails)
53
+ ::Rails.root
54
+ else
55
+ Dir.pwd
56
+ end
57
+ end
58
+
59
+ end
60
+
61
+ end
@@ -0,0 +1,7 @@
1
+ module TouRETS
2
+
3
+ class ConfigurationError < StandardError; end
4
+
5
+ class ConnectionError < StandardError; end
6
+
7
+ end
@@ -0,0 +1,20 @@
1
+ # This is only a temporary fix until it's patched in Rails
2
+ class Hash
3
+
4
+ def recursive_symbolize_keys!
5
+ symbolize_keys!
6
+
7
+ values.each{|h| h.recursive_symbolize_keys! if h.is_a?(Hash)}
8
+ values.select{|v| v.is_a?(Array)}.flatten.each{|h| h.recursive_symbolize_keys! if h.is_a?(Hash)}
9
+ self
10
+ end
11
+
12
+ #In case for some reason this isn't picked up from Rails
13
+ def symbolize_keys!
14
+ keys.each do |key|
15
+ self[(key.to_sym rescue key) || key] = delete(key)
16
+ end
17
+ self
18
+ end
19
+
20
+ end
@@ -0,0 +1,53 @@
1
+ module TouRETS
2
+ class Photo
3
+
4
+ class << self
5
+
6
+ # Find the photo
7
+ # Use find("1234", :id => 1) to find the photo with ID 1 in the 1234 group
8
+ # Default it to return all of the photos.
9
+ def find(sysid, opts={})
10
+ limit = opts[:id] || "*"
11
+ [].tap do |photos|
12
+ TouRETS.current_connection.get_object(:resource => opts[:resource], :type => :Photo, :location => false, :id => "#{sysid}:#{limit}") do |headers, content|
13
+ photos << new(headers, content)
14
+ end
15
+ end
16
+ end
17
+
18
+ end
19
+
20
+ attr_accessor :id, :resource_id, :content_type, :content
21
+
22
+ # id is the ID of the photo
23
+ # resource_id is the ID of the resource it belongs to. Properties will probably be sysid everytime
24
+ # content_type is the Image content_type
25
+ # content is the Binary string that can be written to a file to display the image.
26
+ def initialize(headers, content)
27
+ self.id = headers["object-id"]
28
+ self.resource_id = headers["content-id"]
29
+ self.content_type = headers["content-type"]
30
+ self.content = content
31
+ end
32
+
33
+ # Use this method to get the binary string to write to a file.
34
+ # Sometimes the string will contin ASCII-8BIT characters.
35
+ # Forcing to UTF-8 will make it write to a file easier.
36
+ def image
37
+ content.to_s.force_encoding("UTF-8")
38
+ end
39
+
40
+ # Returns a filename based on the resource_id and id of the photo
41
+ # should have proper extension on it.
42
+ def filename
43
+ ext = case content_type
44
+ when /jpeg$/, /jpg$/ then "jpg"
45
+ when /png$/ then "png"
46
+ when /gif$/ then "gif"
47
+ else "jpg"
48
+ end
49
+ "#{resource_id}-#{id}.#{ext}"
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,84 @@
1
+ module TouRETS
2
+ class Property
3
+ include Utilities
4
+ extend Utilities
5
+
6
+ SEARCH_QUERY_DEFAULTS = {:active_properties => "ER,EA,C", :idx_display => "Y", :internet_display => "Y"}
7
+ # This class searches for ResidentialProperty, Condo, SingleFamily, Rental
8
+ # Some MLS use "1", some use :RES... Will need to decide which way is to be used.
9
+ SEARCH_CONFIG_DEFAULTS = {:search_type => :Property, :class => "1"}
10
+
11
+ class << self
12
+
13
+ # Returns an array of all of the properties
14
+ # Property.all
15
+ def all
16
+ where
17
+ end
18
+
19
+ # Returns an array of property results.
20
+ # Property.where(:bedrooms => 7, :bathrooms => 4, :list_price => 200000..300000)
21
+ # Property.where(:property_type => {:not => ['CONDO', 'TOWNHOME']}, :area => {:or => ['South West', 'North West']})
22
+ # Property.where(:area => ['South West', 'North West']) # This is like 'AND'
23
+ def where(search_params = {})
24
+ TouRETS.ensure_connected!
25
+ [].tap do |properties|
26
+ search_params = map_search_params(SEARCH_QUERY_DEFAULTS.merge(search_params))
27
+ search_config = SEARCH_CONFIG_DEFAULTS.merge({:query => hash_to_rets_query_string(search_params)})
28
+ Search.find(search_config) do |property|
29
+ properties << self.new(property)
30
+ end
31
+ end
32
+ end
33
+
34
+ # # Propert.where(:bedrooms => 3).limit(10) #not implemented
35
+ # def limit(limit_number = 5000)
36
+ # SEARCH_CONFIG_DEFAULTS.merge!(:limit => limit_number)
37
+ # self
38
+ # end
39
+ #
40
+ # # Property.where(:bedrooms => 3).count #not implemented
41
+ # def count
42
+ # SEARCH_CONFIG_DEFAULTS.merge!(:count_mode => :only)
43
+ # self
44
+ # end
45
+ #
46
+ # # Property.select(['SystemName', 'LongName']).where(:bedrooms => 3) #not implemented
47
+ # # select is to limit which fields actually get returned. This could help with mapping
48
+ # def select(fields=[])
49
+ # {:select => fields}
50
+ # self
51
+ # end
52
+
53
+ end
54
+
55
+ attr_accessor :attributes
56
+
57
+ def initialize(args = {})
58
+ self.attributes = args
59
+ end
60
+
61
+ # Return an array of the photo objects that belong to a particular property
62
+ def photos
63
+ @photos ||= grab_photos
64
+ end
65
+
66
+ def method_missing(method_name, *args, &block)
67
+ val = attributes[key_map[method_name.to_sym]]
68
+ if val.nil?
69
+ super
70
+ else
71
+ return val
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ def grab_photos
78
+ [].tap do |pics|
79
+ pics << TouRETS::Photo.find(attributes['sysid'], :resource => :Property)
80
+ end.flatten
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,10 @@
1
+ require 'rails'
2
+ require 'tourets/rails/engine'
3
+ require 'tourets/rails/railtie'
4
+ require 'tourets/rails/version'
5
+
6
+ module TouRETS
7
+ module Rails
8
+
9
+ end
10
+ end
@@ -0,0 +1,6 @@
1
+ module TouRETS
2
+ module Rails
3
+ class Engine < ::Rails::Engine
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,8 @@
1
+
2
+ module TouRETS
3
+ module Rails
4
+ class Railtie < ::Rails::Railtie
5
+
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,6 @@
1
+ module TouRETS
2
+ module Rails
3
+ VERSION = "0.1.0"
4
+
5
+ end
6
+ end
@@ -0,0 +1,19 @@
1
+ module TouRETS
2
+
3
+ class Search
4
+
5
+ class << self
6
+
7
+ # This works as a raw search. This give the developer a chance
8
+ # to customize how they want to search.
9
+ def find(search_params = {}, &block)
10
+ raise ArgumentError, "No block passed" unless block_given?
11
+ TouRETS.ensure_connected!
12
+ TouRETS.current_connection.search(search_params, &block)
13
+ end
14
+
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -0,0 +1,138 @@
1
+ module TouRETS
2
+ module Utilities
3
+
4
+ # Convert a key value pair into a RETS formatted query string.
5
+ # TODO: Take values that are arrays, ranges, or hashes, and convert those properly
6
+ def hash_to_rets_query_string(hash)
7
+ [].tap do |str|
8
+ hash.each_pair do |k,v|
9
+ val = value_map(v)
10
+ str << "(#{k.to_s.camelize}=#{val})"
11
+ end
12
+ end.join(',')
13
+ end
14
+
15
+ # This takes a hash of search parameters, and modifies
16
+ # the hash to have the correct key types for the current RETS server
17
+ def map_search_params(search_params)
18
+ Hash[search_params.map {|k, v| [key_map[k], v] }]
19
+ end
20
+
21
+ # Giant Hash.
22
+ # TODO: OPTIMIZE!!!! ZOMG! O_o
23
+ # Maybe break this into a YAML file that will pick which keymap to use based on the current_connection?
24
+ # I'm thinking maybe like a dictionary lookup. Figure out what each RETS server uses.
25
+ # Also good to note that there are different data types, but they all are strings.
26
+ # We could figure out a way to convert to Integer, or DateTime objects, etc... depending.
27
+ def key_map
28
+ {
29
+ :id => "sysid", #String
30
+ :property_type => "1", #String
31
+ :acreage => "2", #Float
32
+ :zip_code => "10", #String
33
+ :address => "13", #String
34
+ :partial_flag => "17", #String
35
+ :close_date => "25", #DateTime
36
+ :agent_name => "26", #String
37
+ :annual_property_tax => "28", #Integer
38
+ :has_dishwasher => "30", #Boolean
39
+ :has_disposal => "31", #Boolean
40
+ :has_refrigerator => "33", #Boolean
41
+ :dryer_hookup => "34", #String
42
+ :area => "37", #String
43
+ :association_fee => "39", #Integer
44
+ #1st Encumbr Assumption Desc => "43" # Like the mortgage...
45
+ :three_quarter_baths => "60", #Integer
46
+ :bathrooms => "61", #Integer
47
+ :half_baths => "62", #Integer
48
+ :total_baths => "63", #Integer
49
+ :downstairs_bath_desc => "63",#String
50
+ :bedrooms => "68", #Integer
51
+ :built_desc => "72", #String
52
+ :carport_desc => "73", #String
53
+ :carports => "74", #Integer
54
+ :comp_days_on_market => "81", #Integer (this is nil sometimes)
55
+ :contingency_desc => "84", #String
56
+ :contract_date => "85", #DateTime
57
+ :cooling_fuel_desc => "86", #String
58
+ :county => "87", #String
59
+ :second_bed_dim => "89", #String
60
+ :third_bed_dim => "90", #String
61
+ :fourth_bed_dim => "91", #String
62
+ :dining_room_dim => "92", #String
63
+ :family_room_dim => "93", #String
64
+ :fifth_bed_dim => "94", #String
65
+ :living_room_dim => "95", #String
66
+ :master_bed_dim => "96", #String
67
+ :fifth_bed_desc => "97", #String
68
+ :days_on_market => "101", #Integer
69
+ :entry_date => "104", #DateTime
70
+ :fence => "112", #String
71
+ :fireplaces => "113", #Integer
72
+ :converted_garage? => "120", #Boolean
73
+ :garages => "122", #Integer
74
+ :ground_mounted? => "125", #Boolean
75
+ :images => "129", #Integer
76
+ :internet_display => "130", #Boolean
77
+ :land_use => "132", #Integer
78
+ :last_transaction_code => "134", #String
79
+ :last_transaction_at => "135",#DateTime
80
+ :list_office_code => "137", #String
81
+ :listed_on => "138", #DateTime
82
+ :agentcode => "143", #String
83
+ :list_price => "144", #Float
84
+ :agent_member_id => "145", #String
85
+ :lot_sqft => "154", #Integer
86
+ :community => "155", #String
87
+ :listing_number => "163", #String
88
+ :occupancy_desc => "170", #String
89
+ :listing_office => "171", #String
90
+ :listing_office_phone => "172", #String
91
+ :original_list_price => "173",#Float
92
+ :parcel_number => "176", #String
93
+ :photo_instructions => "182", #String
94
+ :pools => "184", #Integer
95
+ :property_condition => "202", #String
96
+ :has_pool => "203", #Boolean
97
+ :record_deleted_at => "205", #DateTime
98
+ :record_deleted => "207", #Boolean
99
+ :buyer_broker => "211", #String
100
+ :elementary_school_2 => "213",#String
101
+ :highschool => "214", #String
102
+ :middle_school => "215", #String
103
+ :year_round_school => "216", #String
104
+ :buyer_agentcode => "218", #String
105
+ :sewer => "219", #String
106
+ # #Loft => "231", #Integer
107
+ :has_spa => "236", #Boolean
108
+ :active_properties => "242",
109
+ :idx_display => "1809",
110
+ :sqft => "2361"
111
+ }
112
+ end
113
+
114
+ # Take values like true and false, convert them to "Y" or "N". make collections into joint strings.
115
+ def value_map(value)
116
+ v = case value.class
117
+ when Array
118
+ value.join(',')
119
+ when Range
120
+ "#{value.first}-#{value.last}"
121
+ when Hash
122
+ if value.has_key?(:or)
123
+ "|#{value[:or].join(',')}"
124
+ elsif value.has_key?(:not)
125
+ "~#{value[:not].join(',')}"
126
+ end
127
+ when TrueClass
128
+ "Y" # TODO: figure out if this should be Y or Yes
129
+ when FalseClass
130
+ "N" # TODO: figure out if this should be N or No
131
+ else
132
+ value
133
+ end
134
+ v
135
+ end
136
+
137
+ end
138
+ end
@@ -0,0 +1,30 @@
1
+ # NOTE: This class is not really implemented yet.
2
+
3
+ module TouRETS
4
+
5
+ # VRImage is a multiple-view, possibly-interactive image related to the :resource
6
+ class VRImage
7
+
8
+ class << self
9
+
10
+ # Find the vrimage
11
+ # Use find("1234", :id => 1) to find the photo with ID 1 in the 1234 group
12
+ # Default it to return all of the photos.
13
+ def find(sysid, opts={})
14
+ limit = opts[:id] || "*"
15
+ [].tap do |photos|
16
+ TouRETS.current_connection.get_object(:resource => opts[:resource], :type => :VRImage, :location => false, :id => "#{sysid}:#{limit}") do |headers, content|
17
+ photos << new(headers, content)
18
+ end
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+
25
+ def initialize(headers, content)
26
+
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/tourets/rails/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Jeremy Woertink", "Justin Mitchell"]
6
+ gem.email = ["jeremywoertink@gmail.com", "jmitchell4140@gmail.com"]
7
+ gem.description = "TouRETS is a rails gem used to interface with multiple RETS using the ruby-rets gem"
8
+ gem.summary = "Use RETS with a LOT less hassle"
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "tourets"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = TouRETS::Rails::VERSION
17
+
18
+ gem.add_dependency('ruby-rets', '2.0.5')
19
+ gem.add_dependency('railties', '>= 3.1')
20
+ gem.add_development_dependency('rspec')
21
+ gem.add_development_dependency('rails', '>= 3.1')
22
+ end
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tourets
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jeremy Woertink
9
+ - Justin Mitchell
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-11-16 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: ruby-rets
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - '='
21
+ - !ruby/object:Gem::Version
22
+ version: 2.0.5
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - '='
29
+ - !ruby/object:Gem::Version
30
+ version: 2.0.5
31
+ - !ruby/object:Gem::Dependency
32
+ name: railties
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '3.1'
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '3.1'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ - !ruby/object:Gem::Dependency
64
+ name: rails
65
+ requirement: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '3.1'
71
+ type: :development
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '3.1'
79
+ description: TouRETS is a rails gem used to interface with multiple RETS using the
80
+ ruby-rets gem
81
+ email:
82
+ - jeremywoertink@gmail.com
83
+ - jmitchell4140@gmail.com
84
+ executables: []
85
+ extensions: []
86
+ extra_rdoc_files: []
87
+ files:
88
+ - .gitignore
89
+ - Gemfile
90
+ - LICENSE
91
+ - README.md
92
+ - Rakefile
93
+ - lib/tourets.rb
94
+ - lib/tourets/exceptions.rb
95
+ - lib/tourets/extensions/hash.rb
96
+ - lib/tourets/photo.rb
97
+ - lib/tourets/property.rb
98
+ - lib/tourets/rails.rb
99
+ - lib/tourets/rails/engine.rb
100
+ - lib/tourets/rails/railtie.rb
101
+ - lib/tourets/rails/version.rb
102
+ - lib/tourets/search.rb
103
+ - lib/tourets/utilities.rb
104
+ - lib/tourets/vrimage.rb
105
+ - tourets.gemspec
106
+ homepage: ''
107
+ licenses: []
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ segments:
119
+ - 0
120
+ hash: 3840694011043471350
121
+ required_rubygems_version: !ruby/object:Gem::Requirement
122
+ none: false
123
+ requirements:
124
+ - - ! '>='
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ segments:
128
+ - 0
129
+ hash: 3840694011043471350
130
+ requirements: []
131
+ rubyforge_project:
132
+ rubygems_version: 1.8.24
133
+ signing_key:
134
+ specification_version: 3
135
+ summary: Use RETS with a LOT less hassle
136
+ test_files: []