lib/mls.rb in mls-0.14.0 vs lib/mls.rb in mls-1.0.0

- old
+ new

@@ -1,456 +1,102 @@ -require 'uri' -require 'cgi' -require 'logger' -require 'net/https' -require 'singleton' -require 'yajl' -require 'bigdecimal' -require 'bigdecimal/util' -require 'active_support' -require 'active_support/core_ext' -require 'date' -require 'time' +require 'sunstone' -class Decimal #:nodoc: -end +module MLS -class Boolean #:nodoc: -end - -# _MLS_ is a low-level API. It provides basic HTTP #get, #post, #put, and #delete -# calls to the MLS. It can also provides basic error checking of responses. -class MLS - include Singleton - API_VERSION = '0.1.0' - attr_reader :url, :user_agent - attr_writer :asset_host, :image_host, :agent_profile - attr_accessor :api_key, :cookie_jar, :logger, :branch, :server, :forwarded_for - - # Sets the API Token and Host of the MLS Server - # - # #!ruby - # MLS.url = "https://mls.42floors.com/API_KEY" - def url=(uri) # TODO: testme - @url = URI.parse(uri) - @api_key = CGI.unescape(@url.user) - @host, @port = @url.host, @url.port - @ssl = (url.scheme == 'https') + def request_headers + super + super.merge({ + 'Api-Version' => API_VERSION + }) end - # Sets the user agent so that MLS can distinguish between multiple users - # with the same auth - def user_agent=(user_agent) - @user_agent = user_agent + def self.asset_host + MLS::Model.connection.server_config['asset_host'] end - def logger # TODO: testme - @logger ||= default_logger + def self.image_host + MLS::Model.connection.server_config['image_host'] end + +end - # Returns the current connection to the MLS or if connection has been made - # it returns a new connection - def connection # TODO: testme - return @connection if @connection - @connection = Net::HTTP.new(@host, @port) - @connection.use_ssl = @ssl - @connection - end +class MLS::Model < ActiveRecord::Base + self.abstract_class = true +end - # provides the asset host, if asset_host is set then it is returned, - # otherwise it queries the MLS for this configuration. - def asset_host # TODO: testme - @asset_host ||= get('/asset_host').body - end +module MLS::Slugger + + extend ActiveSupport::Concern + + module ClassMethods + + def find(*args) + friendly = -> (arg) { arg.respond_to?(:to_i) && arg.to_i.to_s != arg.to_s } - def image_host # TODO: testme - raw_image_host % (rand(4)) - end - - def raw_image_host - @image_host ||= get('/image_host').body - end - - def agent_profile(id) - @agent_profile = Yajl::Parser.new(:symbolize_keys => true) - .parse(MLS.get("/agents/#{id}").body) - end - - def headers # TODO: testme - headers = { - 'Content-Type' => 'application/json', - 'User-Agent' => @user_agent, - 'X-42Floors-API-Version' => API_VERSION, - 'X-42Floors-API-Key' => api_key - } - headers['X-42Floors-Branch'] = branch if branch - headers['X-42Floors-Server'] = server if server - headers['X-Forwarded-For'] = forwarded_for if forwarded_for - headers - end - - def prepare_request(req) # TODO: testme - headers.each { |k, v| req[k] = v } - req['Cookie'] = cookie_jar[:api_session] if cookie_jar[:api_session] - end - - # Gets to +url+ on the MLS Server. Automatically includes any headers returned - # by the MLS#headers function. - # - # Paramaters:: - # - # * +url+ - The +url+ on the server to Get to. To get to <tt>"/accounts"</tt> - # pass <tt>"/accounts"</tt> as +url+ - # * +params+ - A Hash or Ruby Object that responds to #to_param. The result - # of this method is appended on the URL as query params - # * +valid_response_codes+ - An Array of HTTP response codes that should be - # considered accepable and not raise exceptions. For example If you don't - # want a MLS::Exception::NotFound to be raised when a GET request returns - # a 404 pass in 404, and the response body will be returned if the status - # code is a 404 as it does if the status code is in the 200..299 rage. Status - # codes in the 200..299 range are *always* considred acceptable - # - # Return Value:: - # - # Returns the return value of the <tt>&block</tt> if given, otherwise the response - # object - # - # Examples: - # - # #!ruby - # MLS.get('/example') # => #<Net::HTTP::Response> - # - # MLS.get('/example', {:body => 'stuff'}) # => #<Net::HTTP::Response> - # - # MLS.get('/404') # => raises MLS::Exception::NotFound - # - # MLS.get('/404', nil, 404, 450..499) # => #<Net::HTTP::Response> - # - # MLS.get('/404', nil, [404, 450..499]) # => #<Net::HTTP::Response> - # - # MLS.get('/404', nil, 404) # => #<Net::HTTP::Response> - # - # # this will still raise an exception if the response_code is not valid - # # and the block will not be called - # MLS.get('/act') do |response, response_code| - # # ... - # end - def get(url, params={}, *valid_response_codes, &block) - params ||= {} - - req = Net::HTTP::Get.new(url + '?' + params.to_param) - prepare_request(req) - - response = connection.request(req) - handle_response(response, valid_response_codes) - - response.body.force_encoding(Encoding::UTF_8) - if block_given? - yield(response, response.code.to_i) - else - response + if args.count == 1 && friendly.call(args.first) + find_by_slug!(args) + else + super + end end - end - # Puts to +url+ on the MLS Server. Automatically includes any headers returned - # by the MLS#headers function. - # - # Paramaters:: - # - # * +url+ - The +url+ on the server to Put to. To put to <tt>"/accounts"</tt> - # pass <tt>"/accounts"</tt> as +url+ - # * +body+ - A Ruby object which is converted into JSON and added in the request - # Body. - # * +valid_response_codes+ - An Array of HTTP response codes that should be - # considered accepable and not raise exceptions. For example If you don't - # want a MLS::Exception::NotFound to be raised when a PUT request returns - # a 404 pass in 404, and the response body will be returned if the status - # code is a 404 as it does if the status code is in the 200..299 rage. Status - # codes in the 200..299 range are *always* considred acceptable - # - # Return Value:: - # - # Returns the return value of the <tt>&block</tt> if given, otherwise the response - # object - # - # Examples: - # - # #!ruby - # MLS.put('/example') # => #<Net::HTTP::Response> - # - # MLS.put('/example', {:body => 'stuff'}) # => #<Net::HTTP::Response> - # - # MLS.put('/404') # => raises MLS::Exception::NotFound - # - # MLS.put('/404', nil, 404, 450..499) # => #<Net::HTTP::Response> - # - # MLS.put('/404', nil, [404, 450..499]) # => #<Net::HTTP::Response> - # - # MLS.put('/404', nil, 404) # => #<Net::HTTP::Response> - # - # # this will still raise an exception if the response_code is not valid - # # and the block will not be called - # MLS.put('/act') do |response, response_code| - # # ... - # end - def put(url, body={}, *valid_response_codes, &block) - body ||= {} - - req = Net::HTTP::Put.new(url) - req.body = Yajl::Encoder.encode(body) - prepare_request(req) - - response = connection.request(req) - handle_response(response, valid_response_codes) - - if block_given? - yield(response, response.code.to_i) - else - response - end end - - # Posts to +url+ on the MLS Server. Automatically includes any headers returned - # by the MLS#headers function. - # - # Paramaters:: - # - # * +url+ - The +url+ on the server to Post to. To post to <tt>"/accounts"</tt> - # pass <tt>"/accounts"</tt> as +url+ - # * +body+ - A Ruby object which is converted into JSON and added in the request - # Body. - # * +valid_response_codes+ - An Array of HTTP response codes that should be - # considered accepable and not raise exceptions. For example If you don't - # want a MLS::Exception::NotFound to be raised when a POST request returns - # a 404 pass in 404, and the response body will be returned if the status - # code is a 404 as it does if the status code is in the 200..299 rage. Status - # codes in the 200..299 range are *always* considred acceptable - # - # Return Value:: - # - # Returns the return value of the <tt>&block</tt> if given, otherwise the response - # object - # - # Examples: - # - # #!ruby - # MLS.post('/example') # => #<Net::HTTP::Response> - # - # MLS.post('/example', {:body => 'stuff'}) # => #<Net::HTTP::Response> - # - # MLS.post('/404') # => raises MLS::Exception::NotFound - # - # MLS.post('/404', nil, 404, 450..499) # => #<Net::HTTP::Response> - # - # MLS.post('/404', nil, [404, 450..499]) # => #<Net::HTTP::Response> - # - # MLS.post('/404', nil, 404) # => #<Net::HTTP::Response> - # - # # this will still raise an exception if the response_code is not valid - # # and the block will not be called - # MLS.post('/act') do |response, response_code| - # # ... - # end - def post(url, body={}, *valid_response_codes, &block) - body ||= {} - - req = Net::HTTP::Post.new(url) - req.body = Yajl::Encoder.encode(body) - prepare_request(req) - - response = connection.request(req) - handle_response(response, valid_response_codes) - - if block_given? - yield(response, response.code.to_i) - else - response - end + + def to_param + slug end - # Deletes to +url+ on the MLS Server. Automatically includes any headers returned - # by the MLS#headers function. - # - # Paramaters:: - # - # * +url+ - The +url+ on the server to Post to. To delete to <tt>"/accounts"</tt> - # pass <tt>"/accounts"</tt> as +url+ - # * +body+ - A Ruby object which is converted into JSON and added in the request - # Body. - # * +valid_response_codes+ - An Array of HTTP response codes that should be - # considered accepable and not raise exceptions. For example If you don't - # want a MLS::Exception::NotFound to be raised when a POST request returns - # a 404 pass in 404, and the response body will be returned if the status - # code is a 404 as it does if the status code is in the 200..299 rage. Status - # codes in the 200..299 range are *always* considred acceptable - # - # Return Value:: - # - # Returns the return value of the <tt>&block</tt> if given, otherwise the - # response object - # - # Examples: - # - # #!ruby - # MLS.delete('/example') # => #<Net::HTTP::Response> - # - # MLS.delete('/example', {:body => 'stuff'}) # => #<Net::HTTP::Response> - # - # MLS.delete('/404') # => raises MLS::Exception::NotFound - # - # MLS.delete('/404', nil, 404, 450..499) # => #<Net::HTTP::Response> - # - # MLS.delete('/404', nil, [404, 450..499]) # => #<Net::HTTP::Response> - # - # MLS.delete('/404', nil, 404) # => #<Net::HTTP::Response> - # - # # this will still raise an exception if the response_code is not valid - # # and the block will not be called - # MLS.delete('/act') do |response, response_code| - # # ... - # end - def delete(url, body={}, *valid_response_codes, &block) - body ||= {} +end - req = Net::HTTP::Delete.new(url) - req.body = Yajl::Encoder.encode(body) - prepare_request(req) +module MLS::Avatar - response = connection.request(req) - handle_response(response, valid_response_codes) - if block_given? - yield(response, response.code.to_i) - else - response - end - end + extend ActiveSupport::Concern - # Raise an MLS::Exception based on the response_code, unless the response_code - # is include in the valid_response_codes Array - # - # Paramaters:: - # - # * +response+ - The Net::HTTP::Response object - # * +valid_response_codes+ - An Array, Integer, or Range. If it's Array the - # Array can include both Integers or Ranges. - # - # Return Value:: - # - # If an exception is not raised the +response+ is returned - # - # Examples: - # - # #!ruby - # MLS.handle_response(<Net::HTTP::Response @code=200>) # => <Net::HTTP::Response @code=200> - # - # MLS.handle_response(<Net::HTTP::Response @code=404>) # => raises MLS::Exception::NotFound - # - # MLS.handle_response(<Net::HTTP::Response @code=500>) # => raises MLS::Exception - # - # MLS.handle_response(<Net::HTTP::Response @code=404>, 404) # => <Net::HTTP::Response @code=404> - # - # MLS.handle_response(<Net::HTTP::Response @code=500>, 404, 500) # => <Net::HTTP::Response @code=500> - # - # MLS.handle_response(<Net::HTTP::Response @code=405>, 300, 400..499) # => <Net::HTTP::Response @code=405> - # - # MLS.handle_response(<Net::HTTP::Response @code=405>, [300, 400..499]) # => <Net::HTTP::Response @code=405> - def handle_response(response, *valid_response_codes) - if response['X-42Floors-API-Version-Deprecated'] - logger.warn("DEPRECATION WARNING: API v#{API_VERSION} is being phased out") - end - - code = response.code.to_i - valid_response_codes.flatten! - valid_response_codes << (200..299) - - if !valid_response_codes.detect{|i| i.is_a?(Range) ? i.include?(code) : i == code} - case code - when 400 - raise MLS::Exception::BadRequest, response.body - when 401 - raise MLS::Exception::Unauthorized, response.body - when 404 - raise MLS::Exception::NotFound - when 410 - raise MLS::Exception::Gone - when 422 - raise MLS::Exception::ApiVersionUnsupported, response.body - when 503 - raise MLS::Exception::ServiceUnavailable, response.body - when 301 - raise MLS::Exception::MovedPermanently, response.body - when 300..599 - raise MLS::Exception, code - end - end - - cookie_jar[:api_session] = response['Set-Cookie'] if response['Set-Cookie'] - cookie_jar[:api_session_id] = response['X-42Floors-API-Session-Id'] if response['X-42Floors-API-Session-Id'] - - response + included do + belongs_to :avatar, :class_name => 'Photo' end - # Ping the MLS. If everything is configured and operating correctly <tt>"pong"</tt> - # will be returned. Otherwise and MLS::Exception should be thrown. - # - # #!ruby - # MLS.ping # => "pong" - # - # MLS.ping # raises MLS::Exception::ServiceUnavailable if a 503 is returned - def ping # TODO: testme - get('/ping').body - end + def avatar_url(options={}) + options.reverse_merge!({ + :style => nil, + :protocol => "http", + :bg => nil, + :format => "jpg" + }); - def auth_ping # TODO: testme - post('/ping').body - end + url_params = {s: options[:style], bg: options[:bg]}.select{ |k, v| v } + result = "#{options[:protocol]}://#{MLS.image_host}/#{avatar_digest}.#{options[:format]}" + result += "?#{url_params.to_param}" if url_params.size > 1 - def default_logger # TODO: testme - logger = Logger.new(STDOUT) - logger.level = Logger::INFO - logger + result end - # Delegates all uncauge class method calls to the singleton - def self.method_missing(method, *args, &block) #:nodoc: # TODO: testme - instance.__send__(method, *args, &block) - end - - def self.parse(json) # TODO: testme - Yajl::Parser.new(:symbolize_keys => true).parse(json) - end - end -require 'mls/errors' -require 'mls/resource' -require 'mls/parser' +require 'mls/photo' +require 'mls/account' +require 'mls/brokerage' +require 'mls/property' +require 'mls/region' +require 'mls/listing' +require 'mls/lease' +require 'mls/sublease' +require 'mls/space' +require 'mls/sale' +require 'mls/coworking_space' +require 'mls/address' +require 'mls/locality' +require 'mls/flyer' +require 'mls/agency' +require 'mls/floorplan' +require 'mls/use' -# Attributes -require 'mls/attribute' -require 'mls/attributes/fixnum' -require 'mls/attributes/boolean' -require 'mls/attributes/decimal' -require 'mls/attributes/datetime' -require 'mls/attributes/string' -require 'mls/attributes/hash' -require 'mls/attributes/array' - # Models -require 'mls/model' -require 'mls/models/account' -require 'mls/models/listing' -require 'mls/models/address' -require 'mls/models/property' -require 'mls/models/photo' -require 'mls/models/video' -require 'mls/models/pdf' -require 'mls/models/tour' -require 'mls/models/flyer' -require 'mls/models/floorplan' -require 'mls/models/region' -require 'mls/models/brokerage' - -#factories -require 'mls/factories_helper' +# # Helpers +# class MLS +# +# def current_account +# end +# +# end \ No newline at end of file