oh 1.0.1
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/.autotest +23 -0
 - data/History.txt +6 -0
 - data/Manifest.txt +14 -0
 - data/README.rdoc +67 -0
 - data/Rakefile +12 -0
 - data/bin/oh +57 -0
 - data/lib/oh.rb +202 -0
 - data/lib/ohbjects.rb +67 -0
 - data/lib/ohbjects/account.rb +29 -0
 - data/lib/ohbjects/options.rb +78 -0
 - data/lib/ohbjects/quote.rb +35 -0
 - data/test/test_oh.rb +166 -0
 - data/test/test_ohbjects.rb +63 -0
 - data/test/test_options.rb +69 -0
 - metadata +133 -0
 
    
        data/.autotest
    ADDED
    
    | 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # -*- ruby -*-
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'autotest/restart'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            # Autotest.add_hook :initialize do |at|
         
     | 
| 
      
 6 
     | 
    
         
            +
            #   at.extra_files << "../some/external/dependency.rb"
         
     | 
| 
      
 7 
     | 
    
         
            +
            #
         
     | 
| 
      
 8 
     | 
    
         
            +
            #   at.libs << ":../some/external"
         
     | 
| 
      
 9 
     | 
    
         
            +
            #
         
     | 
| 
      
 10 
     | 
    
         
            +
            #   at.add_exception 'vendor'
         
     | 
| 
      
 11 
     | 
    
         
            +
            #
         
     | 
| 
      
 12 
     | 
    
         
            +
            #   at.add_mapping(/dependency.rb/) do |f, _|
         
     | 
| 
      
 13 
     | 
    
         
            +
            #     at.files_matching(/test_.*rb$/)
         
     | 
| 
      
 14 
     | 
    
         
            +
            #   end
         
     | 
| 
      
 15 
     | 
    
         
            +
            #
         
     | 
| 
      
 16 
     | 
    
         
            +
            #   %w(TestA TestB).each do |klass|
         
     | 
| 
      
 17 
     | 
    
         
            +
            #     at.extra_class_map[klass] = "test/test_misc.rb"
         
     | 
| 
      
 18 
     | 
    
         
            +
            #   end
         
     | 
| 
      
 19 
     | 
    
         
            +
            # end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            # Autotest.add_hook :run_command do |at|
         
     | 
| 
      
 22 
     | 
    
         
            +
            #   system "rake build"
         
     | 
| 
      
 23 
     | 
    
         
            +
            # end
         
     | 
    
        data/History.txt
    ADDED
    
    
    
        data/Manifest.txt
    ADDED
    
    
    
        data/README.rdoc
    ADDED
    
    | 
         @@ -0,0 +1,67 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            = oh
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            * http://github.com/aasmith/oh
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            == Description
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            An API for OptionsHouse (http://optionshouse.com).
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            Currently provides a mechanism for pulling stock and option quotes.
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            == Synopsis
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              # The basic oh API. Calls to the API return parsed Nokogiri documents.
         
     | 
| 
      
 14 
     | 
    
         
            +
              require 'oh'
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              # An object wrapper around the basic api. Marshals above responses into
         
     | 
| 
      
 17 
     | 
    
         
            +
              # handy Ruby objects, defined in lib/ohbjects.
         
     | 
| 
      
 18 
     | 
    
         
            +
              require 'ohbjects'
         
     | 
| 
      
 19 
     | 
    
         
            +
              Ohbjects.activate
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
              # You defined these vars somewhere else, right?
         
     | 
| 
      
 22 
     | 
    
         
            +
              o = Oh.new(username, password)
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
              # Use the virtual account so we don't accidentally spend all your
         
     | 
| 
      
 25 
     | 
    
         
            +
              # hard-earned dollars on Frozen Pork Belly futures.
         
     | 
| 
      
 26 
     | 
    
         
            +
              account = o.accounts.detect { |a| a.virtual? }
         
     | 
| 
      
 27 
     | 
    
         
            +
              o.account_id = account.id
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
              # Bask is the glory of knowing the latest price for the 
         
     | 
| 
      
 30 
     | 
    
         
            +
              # iPath Dow Jones-AIG Coffee Total Return Sub-Index ETN.
         
     | 
| 
      
 31 
     | 
    
         
            +
              p o.quote("JO")
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
              # Do something like this every 120 seconds or so, otherwise your
         
     | 
| 
      
 34 
     | 
    
         
            +
              # token will expire.
         
     | 
| 
      
 35 
     | 
    
         
            +
              o.keep_alive
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            == Requirements
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            *  Nokogiri
         
     | 
| 
      
 40 
     | 
    
         
            +
            *  An OptionsHouse account
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            == Install
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
              sudo gem install oh
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
            == License
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
            Copyright (c) 2010 Andrew A. Smith
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            Permission is hereby granted, free of charge, to any person obtaining
         
     | 
| 
      
 51 
     | 
    
         
            +
            a copy of this software and associated documentation files (the
         
     | 
| 
      
 52 
     | 
    
         
            +
            'Software'), to deal in the Software without restriction, including
         
     | 
| 
      
 53 
     | 
    
         
            +
            without limitation the rights to use, copy, modify, merge, publish,
         
     | 
| 
      
 54 
     | 
    
         
            +
            distribute, sublicense, and/or sell copies of the Software, and to
         
     | 
| 
      
 55 
     | 
    
         
            +
            permit persons to whom the Software is furnished to do so, subject to
         
     | 
| 
      
 56 
     | 
    
         
            +
            the following conditions:
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
            The above copyright notice and this permission notice shall be
         
     | 
| 
      
 59 
     | 
    
         
            +
            included in all copies or substantial portions of the Software.
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
            THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
         
     | 
| 
      
 62 
     | 
    
         
            +
            EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         
     | 
| 
      
 63 
     | 
    
         
            +
            MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
         
     | 
| 
      
 64 
     | 
    
         
            +
            IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
         
     | 
| 
      
 65 
     | 
    
         
            +
            CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
         
     | 
| 
      
 66 
     | 
    
         
            +
            TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
         
     | 
| 
      
 67 
     | 
    
         
            +
            SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         
     | 
    
        data/Rakefile
    ADDED
    
    
    
        data/bin/oh
    ADDED
    
    | 
         @@ -0,0 +1,57 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'oh'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'ohbjects'
         
     | 
| 
      
 5 
     | 
    
         
            +
            Ohbjects.activate
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            username, *symbols = ARGV
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            abort "Usage: #{$0} [username] [symbol ...]" unless username and !symbols.empty?
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            begin
         
     | 
| 
      
 12 
     | 
    
         
            +
              print "Password: "
         
     | 
| 
      
 13 
     | 
    
         
            +
              system "stty -echo"
         
     | 
| 
      
 14 
     | 
    
         
            +
              password = $stdin.gets.chomp
         
     | 
| 
      
 15 
     | 
    
         
            +
            ensure
         
     | 
| 
      
 16 
     | 
    
         
            +
              system "stty echo"
         
     | 
| 
      
 17 
     | 
    
         
            +
              puts
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            o = Oh.new(username, password)
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            account = o.accounts.detect { |a| a.virtual? }
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            if account
         
     | 
| 
      
 25 
     | 
    
         
            +
              puts "Using account #{account.name}"
         
     | 
| 
      
 26 
     | 
    
         
            +
            else
         
     | 
| 
      
 27 
     | 
    
         
            +
              abort "No virtual accounts found for #{username.inspect}"
         
     | 
| 
      
 28 
     | 
    
         
            +
            end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            o.account_id = account.id
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            Thread.abort_on_exception = true
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            auth_last_sent_at = Time.now
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
            begin
         
     | 
| 
      
 37 
     | 
    
         
            +
              puts "Started polling at #{st=Time.now}"
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
              t = Thread.new do
         
     | 
| 
      
 40 
     | 
    
         
            +
                loop do
         
     | 
| 
      
 41 
     | 
    
         
            +
                  symbols.each do |symbol|
         
     | 
| 
      
 42 
     | 
    
         
            +
                    p o.quote(symbol)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                  if Time.now - auth_last_sent_at > 120
         
     | 
| 
      
 46 
     | 
    
         
            +
                    o.keep_alive
         
     | 
| 
      
 47 
     | 
    
         
            +
                    auth_last_sent_at = Time.now
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
              
         
     | 
| 
      
 50 
     | 
    
         
            +
                  sleep 1
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
              t.join
         
     | 
| 
      
 55 
     | 
    
         
            +
            ensure
         
     | 
| 
      
 56 
     | 
    
         
            +
              abort "Terminated at #{et=Time.now} after #{et-st} secs."
         
     | 
| 
      
 57 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/oh.rb
    ADDED
    
    | 
         @@ -0,0 +1,202 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'net/http'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'net/https'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            require 'stringio'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'zlib'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            require 'rubygems'
         
     | 
| 
      
 8 
     | 
    
         
            +
            require 'nokogiri'
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            class Oh
         
     | 
| 
      
 11 
     | 
    
         
            +
              VERSION = '1.0.1'
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              HEADERS = {
         
     | 
| 
      
 14 
     | 
    
         
            +
                "Host" => "www.optionshouse.com",
         
     | 
| 
      
 15 
     | 
    
         
            +
                "User-Agent" => "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12 (.NET CLR 3.5.30729)",
         
     | 
| 
      
 16 
     | 
    
         
            +
                "Accept" => "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
         
     | 
| 
      
 17 
     | 
    
         
            +
                "Accept-Language" => "en-us,en;q=0.5",
         
     | 
| 
      
 18 
     | 
    
         
            +
                "Accept-Encoding" => "gzip,deflate",
         
     | 
| 
      
 19 
     | 
    
         
            +
                "Accept-Charset" => "ISO-8859-1,utf-8;q=0.7,*;q=0.7",
         
     | 
| 
      
 20 
     | 
    
         
            +
                "Connection" => "keep-alive",
         
     | 
| 
      
 21 
     | 
    
         
            +
                "Content-Type" => "text/xml; charset=UTF-8",
         
     | 
| 
      
 22 
     | 
    
         
            +
                "Referer" => "https://www.optionshouse.com/tool/login/",
         
     | 
| 
      
 23 
     | 
    
         
            +
                "Cookie" => "blackbird={pos:1,size:0,load:null,info:true,debug:true,warn:true,error:true,profile:true}",
         
     | 
| 
      
 24 
     | 
    
         
            +
                "Pragma" => "no-cache",
         
     | 
| 
      
 25 
     | 
    
         
            +
                "Cache-Control" => "no-cache",
         
     | 
| 
      
 26 
     | 
    
         
            +
              }
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
              attr_accessor :username, :password
         
     | 
| 
      
 29 
     | 
    
         
            +
              attr_writer :account_id
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              def initialize(username, password)
         
     | 
| 
      
 32 
     | 
    
         
            +
                self.username = username
         
     | 
| 
      
 33 
     | 
    
         
            +
                self.password = password
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              def account_id
         
     | 
| 
      
 37 
     | 
    
         
            +
                @account_id or raise "Account not set; call Oh#accounts() to get a list, then set using Oh#account_id=(id)."
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
              def token
         
     | 
| 
      
 41 
     | 
    
         
            +
                @token ||= login
         
     | 
| 
      
 42 
     | 
    
         
            +
              end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
              def login
         
     | 
| 
      
 45 
     | 
    
         
            +
                response = request_without_callbacks(message("auth.login",
         
     | 
| 
      
 46 
     | 
    
         
            +
                                :userName => username,
         
     | 
| 
      
 47 
     | 
    
         
            +
                                :password => password,
         
     | 
| 
      
 48 
     | 
    
         
            +
                                :organization => "OPHOUSE,KERSHNER",
         
     | 
| 
      
 49 
     | 
    
         
            +
                                :authToken => nil,
         
     | 
| 
      
 50 
     | 
    
         
            +
                                :validationText => ""))
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                access = response.search("//access").text
         
     | 
| 
      
 53 
     | 
    
         
            +
                raise AuthError, "Access was #{access}" unless access == "granted"
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                ready = response.search("//requiresAccountCreation").text == "false"
         
     | 
| 
      
 56 
     | 
    
         
            +
                raise AccountError, "Account is not active" unless ready
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                token = response.search("//authToken").text
         
     | 
| 
      
 59 
     | 
    
         
            +
                raise AuthError, "No auth token was returned" if token.strip.empty? or token == "null"
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                token
         
     | 
| 
      
 62 
     | 
    
         
            +
              end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
              def accounts
         
     | 
| 
      
 65 
     | 
    
         
            +
                request(message_with_token("account.info"))
         
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
              def quote(symbol)
         
     | 
| 
      
 69 
     | 
    
         
            +
                request(messages(quote_messages(symbol)))
         
     | 
| 
      
 70 
     | 
    
         
            +
              end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
              def option_chain(symbol)
         
     | 
| 
      
 73 
     | 
    
         
            +
                request(chain_message(symbol))
         
     | 
| 
      
 74 
     | 
    
         
            +
              end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
              def quote_with_chain(symbol)
         
     | 
| 
      
 77 
     | 
    
         
            +
                request(messages(quote_messages(symbol), chain_message(symbol)))
         
     | 
| 
      
 78 
     | 
    
         
            +
              end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
              # TODO: send this every 120 seconds?
         
     | 
| 
      
 81 
     | 
    
         
            +
              def keep_alive
         
     | 
| 
      
 82 
     | 
    
         
            +
                request(message_with_account("auth.keepAlive"))
         
     | 
| 
      
 83 
     | 
    
         
            +
              end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
              def quote_messages(symbol)
         
     | 
| 
      
 86 
     | 
    
         
            +
                [
         
     | 
| 
      
 87 
     | 
    
         
            +
                  message_with_account("view.quote",
         
     | 
| 
      
 88 
     | 
    
         
            +
                                       :symbol => symbol,
         
     | 
| 
      
 89 
     | 
    
         
            +
                                       :description => true,
         
     | 
| 
      
 90 
     | 
    
         
            +
                                       :fundamentals => true),
         
     | 
| 
      
 91 
     | 
    
         
            +
                  message_with_token("echo", :symbol => symbol)
         
     | 
| 
      
 92 
     | 
    
         
            +
                ]
         
     | 
| 
      
 93 
     | 
    
         
            +
              end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
              def chain_message(symbol)
         
     | 
| 
      
 96 
     | 
    
         
            +
                message_with_account("view.chain",
         
     | 
| 
      
 97 
     | 
    
         
            +
                                     :symbol => symbol,
         
     | 
| 
      
 98 
     | 
    
         
            +
                                     :greeks => true,
         
     | 
| 
      
 99 
     | 
    
         
            +
                                     :weeklies => true,
         
     | 
| 
      
 100 
     | 
    
         
            +
                                     :quarterlies => true,
         
     | 
| 
      
 101 
     | 
    
         
            +
                                     :quotesAfter => 0,
         
     | 
| 
      
 102 
     | 
    
         
            +
                                     :ntm => 10, # near the money
         
     | 
| 
      
 103 
     | 
    
         
            +
                                     :bs => true) # black-scholes?
         
     | 
| 
      
 104 
     | 
    
         
            +
              end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
              def message_with_token(action, data = {})
         
     | 
| 
      
 107 
     | 
    
         
            +
                message(action, {:authToken => token}.merge(data))
         
     | 
| 
      
 108 
     | 
    
         
            +
              end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
              def message_with_account(action, data = {})
         
     | 
| 
      
 111 
     | 
    
         
            +
                message_with_token(action, {:account => account_id}.merge(data))
         
     | 
| 
      
 112 
     | 
    
         
            +
              end
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
              def message(action, data = {})
         
     | 
| 
      
 115 
     | 
    
         
            +
                chunks = []
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                chunks << "<EZMessage action='#{action}'>"
         
     | 
| 
      
 118 
     | 
    
         
            +
                chunks << "<data>"
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                data.each do |key, value|
         
     | 
| 
      
 121 
     | 
    
         
            +
                  chunks << "<#{key}>#{value || "null"}</#{key}>"
         
     | 
| 
      
 122 
     | 
    
         
            +
                end
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                chunks << "</data>"
         
     | 
| 
      
 125 
     | 
    
         
            +
                chunks << "</EZMessage>"
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                chunks.join
         
     | 
| 
      
 128 
     | 
    
         
            +
              end
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
              def messages(*messages)
         
     | 
| 
      
 131 
     | 
    
         
            +
                "<EZList>#{messages.flatten.join}</EZList>"
         
     | 
| 
      
 132 
     | 
    
         
            +
              end
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
              def request_without_callbacks(body)
         
     | 
| 
      
 135 
     | 
    
         
            +
                request(body, false)
         
     | 
| 
      
 136 
     | 
    
         
            +
              end
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
              def request(body, with_callbacks = true)
         
     | 
| 
      
 139 
     | 
    
         
            +
                path = "/m"
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                response = connection.post(path, body, HEADERS)
         
     | 
| 
      
 142 
     | 
    
         
            +
                data = response.body
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
                out = case response["content-encoding"]
         
     | 
| 
      
 145 
     | 
    
         
            +
                  when /gzip/    then Zlib::GzipReader.new(StringIO.new(data)).read
         
     | 
| 
      
 146 
     | 
    
         
            +
                  when /deflate/ then Zlib::Inflate.inflate(data)
         
     | 
| 
      
 147 
     | 
    
         
            +
                  else data
         
     | 
| 
      
 148 
     | 
    
         
            +
                end
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
                if $DEBUG
         
     | 
| 
      
 151 
     | 
    
         
            +
                  puts "Sent:"
         
     | 
| 
      
 152 
     | 
    
         
            +
                  puts body
         
     | 
| 
      
 153 
     | 
    
         
            +
                  puts "-" * 80
         
     | 
| 
      
 154 
     | 
    
         
            +
                  puts "Got"
         
     | 
| 
      
 155 
     | 
    
         
            +
                  p response.code
         
     | 
| 
      
 156 
     | 
    
         
            +
                  p response.message
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                  response.each {|key, val| puts key + ' = ' + val}
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
                  puts out
         
     | 
| 
      
 161 
     | 
    
         
            +
                  puts "=" * 80
         
     | 
| 
      
 162 
     | 
    
         
            +
                  puts
         
     | 
| 
      
 163 
     | 
    
         
            +
                end
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
                result = begin
         
     | 
| 
      
 166 
     | 
    
         
            +
                  Nokogiri.parse(out, nil, nil, Nokogiri::XML::ParseOptions::STRICT)
         
     | 
| 
      
 167 
     | 
    
         
            +
                rescue
         
     | 
| 
      
 168 
     | 
    
         
            +
                  warn "Unable to parse: #{out.inspect}"
         
     | 
| 
      
 169 
     | 
    
         
            +
                  raise
         
     | 
| 
      
 170 
     | 
    
         
            +
                end
         
     | 
| 
      
 171 
     | 
    
         
            +
             
     | 
| 
      
 172 
     | 
    
         
            +
                check_connection_status(result)
         
     | 
| 
      
 173 
     | 
    
         
            +
             
     | 
| 
      
 174 
     | 
    
         
            +
                with_callbacks && respond_to?(:post_process_request) ? 
         
     | 
| 
      
 175 
     | 
    
         
            +
                  post_process_request(result) : 
         
     | 
| 
      
 176 
     | 
    
         
            +
                  result
         
     | 
| 
      
 177 
     | 
    
         
            +
              end
         
     | 
| 
      
 178 
     | 
    
         
            +
             
     | 
| 
      
 179 
     | 
    
         
            +
              def check_connection_status(doc)
         
     | 
| 
      
 180 
     | 
    
         
            +
                if doc.at("//errors/access[text()='denied']")
         
     | 
| 
      
 181 
     | 
    
         
            +
                  raise AuthError, "Access denied, token has expired?"
         
     | 
| 
      
 182 
     | 
    
         
            +
                end
         
     | 
| 
      
 183 
     | 
    
         
            +
              end
         
     | 
| 
      
 184 
     | 
    
         
            +
             
     | 
| 
      
 185 
     | 
    
         
            +
              def connection
         
     | 
| 
      
 186 
     | 
    
         
            +
                return @client if defined? @client
         
     | 
| 
      
 187 
     | 
    
         
            +
             
     | 
| 
      
 188 
     | 
    
         
            +
                client = Net::HTTP.new("www.optionshouse.com", 443)
         
     | 
| 
      
 189 
     | 
    
         
            +
                client.use_ssl = true
         
     | 
| 
      
 190 
     | 
    
         
            +
             
     | 
| 
      
 191 
     | 
    
         
            +
                if ENV["SSL_PATH"] && File.exist?(ENV["SSL_PATH"])
         
     | 
| 
      
 192 
     | 
    
         
            +
                  client.ca_path = ENV["SSL_PATH"]
         
     | 
| 
      
 193 
     | 
    
         
            +
                  client.verify_mode = OpenSSL::SSL::VERIFY_PEER
         
     | 
| 
      
 194 
     | 
    
         
            +
                end
         
     | 
| 
      
 195 
     | 
    
         
            +
             
     | 
| 
      
 196 
     | 
    
         
            +
                @client = client
         
     | 
| 
      
 197 
     | 
    
         
            +
              end
         
     | 
| 
      
 198 
     | 
    
         
            +
             
     | 
| 
      
 199 
     | 
    
         
            +
              AuthError = Class.new(StandardError)
         
     | 
| 
      
 200 
     | 
    
         
            +
              AccountError = Class.new(StandardError)
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/ohbjects.rb
    ADDED
    
    | 
         @@ -0,0 +1,67 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Adds an object representation to Oh responses:
         
     | 
| 
      
 2 
     | 
    
         
            +
            #
         
     | 
| 
      
 3 
     | 
    
         
            +
            #  require "ohbjects"
         
     | 
| 
      
 4 
     | 
    
         
            +
            #  Ohbjects.activate
         
     | 
| 
      
 5 
     | 
    
         
            +
            #
         
     | 
| 
      
 6 
     | 
    
         
            +
            # Then, any further calls to Oh methods that used to
         
     | 
| 
      
 7 
     | 
    
         
            +
            # return an XML doc will now return objects such as
         
     | 
| 
      
 8 
     | 
    
         
            +
            # Ohbjects::Call, Ohbjects::Put, etc.
         
     | 
| 
      
 9 
     | 
    
         
            +
            #
         
     | 
| 
      
 10 
     | 
    
         
            +
            module Ohbjects
         
     | 
| 
      
 11 
     | 
    
         
            +
              REGISTRY = []
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              module Buildable
         
     | 
| 
      
 14 
     | 
    
         
            +
                attr_reader :spec
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                def builds(css_or_xpath)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @spec = css_or_xpath
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def build?(doc)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  !doc.search(@spec).empty?
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                # Taken from active support.
         
     | 
| 
      
 25 
     | 
    
         
            +
                def underscore(camel_cased_word)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  camel_cased_word.to_s.
         
     | 
| 
      
 27 
     | 
    
         
            +
                    gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
         
     | 
| 
      
 28 
     | 
    
         
            +
                    gsub(/([a-z\d])([A-Z])/,'\1_\2').
         
     | 
| 
      
 29 
     | 
    
         
            +
                    tr("-", "_").
         
     | 
| 
      
 30 
     | 
    
         
            +
                    downcase
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def self.extended(extender)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  REGISTRY << extender
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
              def objectify(doc)
         
     | 
| 
      
 39 
     | 
    
         
            +
                qualified_builders = 
         
     | 
| 
      
 40 
     | 
    
         
            +
                  REGISTRY.select { |builder| builder.build?(doc) }
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                objects = []
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                qualified_builders.each do |builder|
         
     | 
| 
      
 45 
     | 
    
         
            +
                  doc.search(builder.spec).each do |fragment|
         
     | 
| 
      
 46 
     | 
    
         
            +
                    objects.push(*builder.build(fragment))
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                objects
         
     | 
| 
      
 51 
     | 
    
         
            +
              end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
              def post_process_request(result)
         
     | 
| 
      
 54 
     | 
    
         
            +
                objectify(result)
         
     | 
| 
      
 55 
     | 
    
         
            +
              end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
              class << self
         
     | 
| 
      
 58 
     | 
    
         
            +
                def activate
         
     | 
| 
      
 59 
     | 
    
         
            +
                  Oh.send :include, self
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
              end
         
     | 
| 
      
 62 
     | 
    
         
            +
            end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
            Dir[File.join(File.dirname(__FILE__), 'ohbjects', '*.rb')].each do |fn|
         
     | 
| 
      
 65 
     | 
    
         
            +
              require fn
         
     | 
| 
      
 66 
     | 
    
         
            +
            end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,29 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Ohbjects
         
     | 
| 
      
 2 
     | 
    
         
            +
              class AccountBuilder
         
     | 
| 
      
 3 
     | 
    
         
            +
                extend Buildable
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                # Not building anything real for now.
         
     | 
| 
      
 6 
     | 
    
         
            +
                builds "//data/account[isVirtual='true']"
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 9 
     | 
    
         
            +
                  def build(fragment)
         
     | 
| 
      
 10 
     | 
    
         
            +
                    account = Account.new
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                    account.id = fragment.at("accountId").text
         
     | 
| 
      
 13 
     | 
    
         
            +
                    account.name = fragment.at("accountName").text
         
     | 
| 
      
 14 
     | 
    
         
            +
                    account.virtual = fragment.at("isVirtual").text =~ /true/
         
     | 
| 
      
 15 
     | 
    
         
            +
                    account.display_id = fragment.at("account").text
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                    account
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              class Account
         
     | 
| 
      
 23 
     | 
    
         
            +
                attr_accessor :id, :display_id, :name, :virtual
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def virtual?
         
     | 
| 
      
 26 
     | 
    
         
            +
                  virtual
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,78 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Ohbjects
         
     | 
| 
      
 2 
     | 
    
         
            +
              class OptionBuilder
         
     | 
| 
      
 3 
     | 
    
         
            +
                extend Buildable
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                builds "optionQuote"
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                METHOD_LOOKUP = {
         
     | 
| 
      
 8 
     | 
    
         
            +
                  :optionRoot  => :root,
         
     | 
| 
      
 9 
     | 
    
         
            +
                  :strikePrice => :strike,
         
     | 
| 
      
 10 
     | 
    
         
            +
                  :expDate     => :expires,
         
     | 
| 
      
 11 
     | 
    
         
            +
                  :bidSize     => :bids,
         
     | 
| 
      
 12 
     | 
    
         
            +
                  :askSize     => :asks
         
     | 
| 
      
 13 
     | 
    
         
            +
                }
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 16 
     | 
    
         
            +
                  def build(fragment)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    put, call = Put.new, Call.new
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                    fragment.attributes.each do |key, attr|
         
     | 
| 
      
 20 
     | 
    
         
            +
                      match = key.match(/^(call|put)?(.*)$/)
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                      option_type, method = match.captures
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                      method = METHOD_LOOKUP[method.to_sym] || underscore(method)
         
     | 
| 
      
 25 
     | 
    
         
            +
                      setter = "#{method}="
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                      value  = attr.value
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                      if option_type
         
     | 
| 
      
 30 
     | 
    
         
            +
                        option = option_type == "call" ? call : put
         
     | 
| 
      
 31 
     | 
    
         
            +
                        option.send(setter, value) if option.respond_to?(setter)
         
     | 
| 
      
 32 
     | 
    
         
            +
                      else
         
     | 
| 
      
 33 
     | 
    
         
            +
                        [put, call].each do |option|
         
     | 
| 
      
 34 
     | 
    
         
            +
                          option.send(setter, value) if option.respond_to?(setter)
         
     | 
| 
      
 35 
     | 
    
         
            +
                        end
         
     | 
| 
      
 36 
     | 
    
         
            +
                      end
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                    [put, call]
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
              end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
              class Option
         
     | 
| 
      
 45 
     | 
    
         
            +
                attr_accessor :id, :key, :strike, :root,
         
     | 
| 
      
 46 
     | 
    
         
            +
                  :bid, :ask, :change, :volume, :open_interest,
         
     | 
| 
      
 47 
     | 
    
         
            +
                  :iv, :delta, :gamma, :theta, :vega,
         
     | 
| 
      
 48 
     | 
    
         
            +
                  :bids, :asks
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                attr_reader :expires
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                def initialize
         
     | 
| 
      
 53 
     | 
    
         
            +
                  if instance_of? Option
         
     | 
| 
      
 54 
     | 
    
         
            +
                    raise ArgumentError, "An option cannot be instantiated"
         
     | 
| 
      
 55 
     | 
    
         
            +
                  end
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                def expires=(date_string)
         
     | 
| 
      
 59 
     | 
    
         
            +
                  # Intentionally explict pattern to force 
         
     | 
| 
      
 60 
     | 
    
         
            +
                  # parse errors should the format change.
         
     | 
| 
      
 61 
     | 
    
         
            +
                  @expires = Date.strptime(date_string, "%b %e, %Y")
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                def call?
         
     | 
| 
      
 65 
     | 
    
         
            +
                  Call === self
         
     | 
| 
      
 66 
     | 
    
         
            +
                end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                def put?
         
     | 
| 
      
 69 
     | 
    
         
            +
                  Put === self
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
              end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
              class Put < Option
         
     | 
| 
      
 74 
     | 
    
         
            +
              end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
              class Call < Option
         
     | 
| 
      
 77 
     | 
    
         
            +
              end
         
     | 
| 
      
 78 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,35 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Ohbjects
         
     | 
| 
      
 2 
     | 
    
         
            +
              class QuoteBuilder
         
     | 
| 
      
 3 
     | 
    
         
            +
                extend Buildable
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                builds "//data/quote"
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 8 
     | 
    
         
            +
                  def build(fragment)
         
     | 
| 
      
 9 
     | 
    
         
            +
                    quote = Quote.new
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                    fragment.attributes.each do |key, attr|
         
     | 
| 
      
 12 
     | 
    
         
            +
                      method = "#{underscore(key)}="
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                      next unless quote.respond_to?(method)
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                      quote.send(method, attr.value)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                    quote.description = 
         
     | 
| 
      
 20 
     | 
    
         
            +
                      fragment.attributes["shortDescription"].value
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                    quote
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              class Quote
         
     | 
| 
      
 28 
     | 
    
         
            +
                attr_accessor :symbol, :bid, :ask, :volume, :prev_close, :last, 
         
     | 
| 
      
 29 
     | 
    
         
            +
                  :open, :high, :low, :today_close, :description
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              # TODO: today_close will be 0 until today has actually closed.
         
     | 
| 
      
 33 
     | 
    
         
            +
              # TODO: many more attributes
         
     | 
| 
      
 34 
     | 
    
         
            +
              # TODO: field conversion
         
     | 
| 
      
 35 
     | 
    
         
            +
            end
         
     | 
    
        data/test/test_oh.rb
    ADDED
    
    | 
         @@ -0,0 +1,166 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "test/unit"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "flexmock/test_unit"
         
     | 
| 
      
 3 
     | 
    
         
            +
            require "oh"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            class TestOh < Test::Unit::TestCase
         
     | 
| 
      
 6 
     | 
    
         
            +
              def setup
         
     | 
| 
      
 7 
     | 
    
         
            +
                @oh = Oh.new("bob", "secret")
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              def mocked_response(data, headers = {})
         
     | 
| 
      
 11 
     | 
    
         
            +
                response = headers.dup
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                class << response
         
     | 
| 
      
 14 
     | 
    
         
            +
                  attr_accessor :body
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                response.body = data
         
     | 
| 
      
 18 
     | 
    
         
            +
                response
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
              def test_request_doesnt_delegate_when_not_present
         
     | 
| 
      
 22 
     | 
    
         
            +
                flexmock(Net::HTTP).new_instances.should_receive(
         
     | 
| 
      
 23 
     | 
    
         
            +
                  :post => mocked_response("<response></response>")
         
     | 
| 
      
 24 
     | 
    
         
            +
                )
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                flexmock(@oh) do |m|
         
     | 
| 
      
 27 
     | 
    
         
            +
                  m.should_receive(:post_process_request).never
         
     | 
| 
      
 28 
     | 
    
         
            +
                  m.should_receive(:respond_to?).
         
     | 
| 
      
 29 
     | 
    
         
            +
                    with(:post_process_request).and_return(false)
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                @oh.request("<request></request>")
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
              def test_request_doesnt_delegate_when_asked_not_to
         
     | 
| 
      
 36 
     | 
    
         
            +
                flexmock(Net::HTTP).new_instances.should_receive(
         
     | 
| 
      
 37 
     | 
    
         
            +
                  :post => mocked_response("<response></response>")
         
     | 
| 
      
 38 
     | 
    
         
            +
                )
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                flexmock(@oh) do |m|
         
     | 
| 
      
 41 
     | 
    
         
            +
                  m.should_receive(:post_process_request).never
         
     | 
| 
      
 42 
     | 
    
         
            +
                  m.should_receive(:respond_to?).
         
     | 
| 
      
 43 
     | 
    
         
            +
                    with(:post_process_request).and_return(true)
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                @oh.request("<request></request>", false)
         
     | 
| 
      
 47 
     | 
    
         
            +
              end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
              def test_request_without_callbacks_calls_request_with_correct_args
         
     | 
| 
      
 50 
     | 
    
         
            +
                flexmock(@oh).should_receive(:request).with("foo", false).once
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                @oh.request_without_callbacks("foo")
         
     | 
| 
      
 53 
     | 
    
         
            +
              end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
              def test_request_delegates
         
     | 
| 
      
 56 
     | 
    
         
            +
                flexmock(Net::HTTP).new_instances.should_receive(
         
     | 
| 
      
 57 
     | 
    
         
            +
                  :post => mocked_response("<response></response>")
         
     | 
| 
      
 58 
     | 
    
         
            +
                )
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                flexmock(@oh) do |m|
         
     | 
| 
      
 61 
     | 
    
         
            +
                  m.should_receive(:post_process_request).once
         
     | 
| 
      
 62 
     | 
    
         
            +
                  m.should_receive(:respond_to?).
         
     | 
| 
      
 63 
     | 
    
         
            +
                    with(:post_process_request).and_return(true)
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                @oh.request("<request></request>")
         
     | 
| 
      
 67 
     | 
    
         
            +
              end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
              def test_request_handles_gzip
         
     | 
| 
      
 70 
     | 
    
         
            +
                io = StringIO.new
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                z = Zlib::GzipWriter.new(io)
         
     | 
| 
      
 73 
     | 
    
         
            +
                z.write "<response></response>"
         
     | 
| 
      
 74 
     | 
    
         
            +
                z.close
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                flexmock(Net::HTTP).new_instances.should_receive(
         
     | 
| 
      
 77 
     | 
    
         
            +
                  :post => mocked_response(
         
     | 
| 
      
 78 
     | 
    
         
            +
                    io.string, 
         
     | 
| 
      
 79 
     | 
    
         
            +
                    "content-encoding" => "gzip"
         
     | 
| 
      
 80 
     | 
    
         
            +
                ))
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                doc = @oh.request("<request></request>")
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                assert_equal "response", doc.root.name,
         
     | 
| 
      
 85 
     | 
    
         
            +
                  "should have document with a root node of response"
         
     | 
| 
      
 86 
     | 
    
         
            +
              end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
              def test_request_handles_deflate
         
     | 
| 
      
 89 
     | 
    
         
            +
                flexmock(Net::HTTP).new_instances.should_receive(
         
     | 
| 
      
 90 
     | 
    
         
            +
                  :post => mocked_response(
         
     | 
| 
      
 91 
     | 
    
         
            +
                    Zlib::Deflate.deflate("<response></response>"),
         
     | 
| 
      
 92 
     | 
    
         
            +
                    "content-encoding" => "deflate"
         
     | 
| 
      
 93 
     | 
    
         
            +
                ))
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                doc = @oh.request("<request></request>")
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                assert_equal "response", doc.root.name,
         
     | 
| 
      
 98 
     | 
    
         
            +
                  "should have document with a root node of response"
         
     | 
| 
      
 99 
     | 
    
         
            +
              end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
              def test_request_handles_plaintext
         
     | 
| 
      
 102 
     | 
    
         
            +
                flexmock(Net::HTTP).new_instances.should_receive(
         
     | 
| 
      
 103 
     | 
    
         
            +
                  :post => mocked_response("<response></response>")
         
     | 
| 
      
 104 
     | 
    
         
            +
                )
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                doc = @oh.request("<request></request>")
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                assert_equal "response", doc.root.name,
         
     | 
| 
      
 109 
     | 
    
         
            +
                  "should have document with a root node of response"
         
     | 
| 
      
 110 
     | 
    
         
            +
              end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
              def test_request_raises_with_malformed_xml
         
     | 
| 
      
 113 
     | 
    
         
            +
                flexmock(Net::HTTP).new_instances.should_receive(
         
     | 
| 
      
 114 
     | 
    
         
            +
                  :post => mocked_response("<invalid></xml>")
         
     | 
| 
      
 115 
     | 
    
         
            +
                )
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                old_stderr = $stderr
         
     | 
| 
      
 118 
     | 
    
         
            +
                $stderr = StringIO.new
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                assert_raise Nokogiri::XML::SyntaxError do
         
     | 
| 
      
 121 
     | 
    
         
            +
                  doc = @oh.request("<request></request>")
         
     | 
| 
      
 122 
     | 
    
         
            +
                end
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                assert_match %r{Unable to parse}, $stderr.string
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                $stderr = old_stderr
         
     | 
| 
      
 127 
     | 
    
         
            +
              end
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
              def test_request_sends_consistent_header_and_path
         
     | 
| 
      
 130 
     | 
    
         
            +
                m = flexmock("Net::HTTP instance")
         
     | 
| 
      
 131 
     | 
    
         
            +
                m.should_ignore_missing
         
     | 
| 
      
 132 
     | 
    
         
            +
                m.should_receive(:post).
         
     | 
| 
      
 133 
     | 
    
         
            +
                  with("/m", "<request></request>", Oh::HEADERS).
         
     | 
| 
      
 134 
     | 
    
         
            +
                  and_return(mocked_response("<response></response>")).
         
     | 
| 
      
 135 
     | 
    
         
            +
                  once
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
                flexmock(Net::HTTP).should_receive(:new).once.and_return(m)
         
     | 
| 
      
 138 
     | 
    
         
            +
             
     | 
| 
      
 139 
     | 
    
         
            +
                @oh.request("<request></request>")
         
     | 
| 
      
 140 
     | 
    
         
            +
              end
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
              def test_connection_reuses_single_client
         
     | 
| 
      
 143 
     | 
    
         
            +
                assert_same @oh.connection, @oh.connection, "Should be cached"
         
     | 
| 
      
 144 
     | 
    
         
            +
              end
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
              def test_message
         
     | 
| 
      
 147 
     | 
    
         
            +
                msg = @oh.message("test.action", :foo => nil, :bar => 2)
         
     | 
| 
      
 148 
     | 
    
         
            +
             
     | 
| 
      
 149 
     | 
    
         
            +
                assert_match %r{<foo>null</foo>}, msg, "should convert nils to null"
         
     | 
| 
      
 150 
     | 
    
         
            +
                assert_match %r{<bar>2</bar>}, msg, "should to_s all other objects"
         
     | 
| 
      
 151 
     | 
    
         
            +
              end
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
              def test_messages
         
     | 
| 
      
 154 
     | 
    
         
            +
                messages = %w(<message_one> <message_two>)
         
     | 
| 
      
 155 
     | 
    
         
            +
                nested_messages = [messages, messages]
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
                assert_equal "<EZList>#{messages.join}</EZList>",
         
     | 
| 
      
 158 
     | 
    
         
            +
                  @oh.messages(messages)
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
                assert_equal "<EZList>#{nested_messages.flatten.join}</EZList>",
         
     | 
| 
      
 161 
     | 
    
         
            +
                  @oh.messages(nested_messages), "should flatten nested messages"
         
     | 
| 
      
 162 
     | 
    
         
            +
              end
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
              def test_account_raises_when_not_set
         
     | 
| 
      
 165 
     | 
    
         
            +
              end
         
     | 
| 
      
 166 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,63 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "test/unit"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "flexmock/test_unit"
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            require "oh"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require "ohbjects"
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            class TestOhbjects < Test::Unit::TestCase
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              def test_objectify
         
     | 
| 
      
 10 
     | 
    
         
            +
                o = Object.new
         
     | 
| 
      
 11 
     | 
    
         
            +
                class << o
         
     | 
| 
      
 12 
     | 
    
         
            +
                  include Ohbjects
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                klass = Class.new
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                klass.instance_eval do
         
     | 
| 
      
 18 
     | 
    
         
            +
                  extend Ohbjects::Buildable
         
     | 
| 
      
 19 
     | 
    
         
            +
                  builds "foo"
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                flexmock(klass).should_receive(:build => "built-foo").once
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                Ohbjects::REGISTRY.clear
         
     | 
| 
      
 25 
     | 
    
         
            +
                Ohbjects::REGISTRY << klass
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                output = o.objectify(Nokogiri("<doc><foo /></doc>"))
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                assert_equal %w(built-foo), output
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              def test_activate_includes_ohbjects_into_oh
         
     | 
| 
      
 33 
     | 
    
         
            +
                flexmock(Oh).should_receive(:include).with(Ohbjects).once
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                Ohbjects.activate
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
              def test_buildable_build?
         
     | 
| 
      
 39 
     | 
    
         
            +
                klass = Class.new
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                klass.instance_eval do
         
     | 
| 
      
 42 
     | 
    
         
            +
                  extend Ohbjects::Buildable
         
     | 
| 
      
 43 
     | 
    
         
            +
                  builds "foo"
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                assert klass.build?(Nokogiri("<doc><foo></foo></doc>"))
         
     | 
| 
      
 47 
     | 
    
         
            +
                assert !klass.build?(Nokogiri("<doc><bar></bar></doc>"))
         
     | 
| 
      
 48 
     | 
    
         
            +
              end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
              def test_buildable_adds_to_registry
         
     | 
| 
      
 51 
     | 
    
         
            +
                before = Ohbjects::REGISTRY.size
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                klass = Class.new
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                klass.instance_eval do
         
     | 
| 
      
 56 
     | 
    
         
            +
                  extend Ohbjects::Buildable
         
     | 
| 
      
 57 
     | 
    
         
            +
                  builds "foo"
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                assert_equal before + 1, Ohbjects::REGISTRY.size
         
     | 
| 
      
 61 
     | 
    
         
            +
                assert_equal klass, Ohbjects::REGISTRY.pop
         
     | 
| 
      
 62 
     | 
    
         
            +
              end
         
     | 
| 
      
 63 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,69 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "test/unit"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "flexmock/test_unit"
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            require "oh"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require "ohbjects"
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            class TestOhbjects < Test::Unit::TestCase
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              def test_option_builder_claims_to_build_option_quote
         
     | 
| 
      
 10 
     | 
    
         
            +
                assert Ohbjects::OptionBuilder.build?(Nokogiri(OPTION_QUOTE))
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              def test_option_builder_build
         
     | 
| 
      
 14 
     | 
    
         
            +
                fragment = Nokogiri(OPTION_QUOTE).search(Ohbjects::OptionBuilder.spec).first
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                options = Ohbjects::OptionBuilder.build(fragment)
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                assert_equal 2, options.size, "Should have built two options"
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                assert_equal [Ohbjects::Put, Ohbjects::Call], options.map{|x|x.class},
         
     | 
| 
      
 21 
     | 
    
         
            +
                  "Should be a put and a call"
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                put = options.shift
         
     | 
| 
      
 24 
     | 
    
         
            +
                call = options.shift
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                # TODO: assert individual fields
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              def test_expires_date_conversion
         
     | 
| 
      
 31 
     | 
    
         
            +
                call = Ohbjects::Call.new
         
     | 
| 
      
 32 
     | 
    
         
            +
                call.expires = "Jan 12, 2011"
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                assert_equal 12, call.expires.day
         
     | 
| 
      
 35 
     | 
    
         
            +
                assert_equal 1, call.expires.month
         
     | 
| 
      
 36 
     | 
    
         
            +
                assert_equal 2011, call.expires.year
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                assert_raise ArgumentError, "other formats should be invalid" do
         
     | 
| 
      
 39 
     | 
    
         
            +
                  call.expires = "2011-01-11"
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
              def test_cannont_instantiate_options_directly
         
     | 
| 
      
 44 
     | 
    
         
            +
                assert_nothing_raised "subclasses should be instantiable" do
         
     | 
| 
      
 45 
     | 
    
         
            +
                  assert_kind_of Ohbjects::Option, Ohbjects::Call.new
         
     | 
| 
      
 46 
     | 
    
         
            +
                  assert_kind_of Ohbjects::Option, Ohbjects::Put.new
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                ex = assert_raise ArgumentError do
         
     | 
| 
      
 50 
     | 
    
         
            +
                  Ohbjects::Option.new
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                assert_match %r{cannot be instantiated}, ex.message
         
     | 
| 
      
 54 
     | 
    
         
            +
              end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
              OPTION_QUOTE = <<-XML
         
     | 
| 
      
 57 
     | 
    
         
            +
                <optionQuote id="201012180000090000AA"
         
     | 
| 
      
 58 
     | 
    
         
            +
                  series="Dec 10 9.0" strikeString="9.0" optionRoot="AA"
         
     | 
| 
      
 59 
     | 
    
         
            +
                  expYear="40" expMonth="12" expDay="18" strikePrice="90000"
         
     | 
| 
      
 60 
     | 
    
         
            +
                  dte="14" expDate="Dec 18, 2010" callKey="AA:20101218:90000:C"
         
     | 
| 
      
 61 
     | 
    
         
            +
                  callBid="5.10" callAsk="5.20" callChange="0.07" callVolume="0"
         
     | 
| 
      
 62 
     | 
    
         
            +
                  callOpenInterest="38" putKey="AA:20101218:90000:P" putBid="0.00"
         
     | 
| 
      
 63 
     | 
    
         
            +
                  putAsk="0.01" putChange="0.00" putVolume="0" putOpenInterest="86"
         
     | 
| 
      
 64 
     | 
    
         
            +
                  callIv="29.1" callDelta="1" callGamma="0" callTheta="0"
         
     | 
| 
      
 65 
     | 
    
         
            +
                  callVega="0" putIv="32.2" putDelta="0" putGamma="0" putTheta="0"
         
     | 
| 
      
 66 
     | 
    
         
            +
                  putVega="0" callBidSize="821" callAskSize="1085" putBidSize="0"
         
     | 
| 
      
 67 
     | 
    
         
            +
                  putAskSize="245" trimSet="2"/>
         
     | 
| 
      
 68 
     | 
    
         
            +
              XML
         
     | 
| 
      
 69 
     | 
    
         
            +
            end
         
     | 
    
        metadata
    ADDED
    
    | 
         @@ -0,0 +1,133 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            --- !ruby/object:Gem::Specification 
         
     | 
| 
      
 2 
     | 
    
         
            +
            name: oh
         
     | 
| 
      
 3 
     | 
    
         
            +
            version: !ruby/object:Gem::Version 
         
     | 
| 
      
 4 
     | 
    
         
            +
              hash: 21
         
     | 
| 
      
 5 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 6 
     | 
    
         
            +
              segments: 
         
     | 
| 
      
 7 
     | 
    
         
            +
              - 1
         
     | 
| 
      
 8 
     | 
    
         
            +
              - 0
         
     | 
| 
      
 9 
     | 
    
         
            +
              - 1
         
     | 
| 
      
 10 
     | 
    
         
            +
              version: 1.0.1
         
     | 
| 
      
 11 
     | 
    
         
            +
            platform: ruby
         
     | 
| 
      
 12 
     | 
    
         
            +
            authors: 
         
     | 
| 
      
 13 
     | 
    
         
            +
            - Andrew A. Smith
         
     | 
| 
      
 14 
     | 
    
         
            +
            autorequire: 
         
     | 
| 
      
 15 
     | 
    
         
            +
            bindir: bin
         
     | 
| 
      
 16 
     | 
    
         
            +
            cert_chain: []
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            date: 2010-12-24 00:00:00 -08:00
         
     | 
| 
      
 19 
     | 
    
         
            +
            default_executable: 
         
     | 
| 
      
 20 
     | 
    
         
            +
            dependencies: 
         
     | 
| 
      
 21 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency 
         
     | 
| 
      
 22 
     | 
    
         
            +
              name: nokogiri
         
     | 
| 
      
 23 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 24 
     | 
    
         
            +
              requirement: &id001 !ruby/object:Gem::Requirement 
         
     | 
| 
      
 25 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 26 
     | 
    
         
            +
                requirements: 
         
     | 
| 
      
 27 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 28 
     | 
    
         
            +
                  - !ruby/object:Gem::Version 
         
     | 
| 
      
 29 
     | 
    
         
            +
                    hash: 3
         
     | 
| 
      
 30 
     | 
    
         
            +
                    segments: 
         
     | 
| 
      
 31 
     | 
    
         
            +
                    - 0
         
     | 
| 
      
 32 
     | 
    
         
            +
                    version: "0"
         
     | 
| 
      
 33 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 34 
     | 
    
         
            +
              version_requirements: *id001
         
     | 
| 
      
 35 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency 
         
     | 
| 
      
 36 
     | 
    
         
            +
              name: rubyforge
         
     | 
| 
      
 37 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 38 
     | 
    
         
            +
              requirement: &id002 !ruby/object:Gem::Requirement 
         
     | 
| 
      
 39 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 40 
     | 
    
         
            +
                requirements: 
         
     | 
| 
      
 41 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 42 
     | 
    
         
            +
                  - !ruby/object:Gem::Version 
         
     | 
| 
      
 43 
     | 
    
         
            +
                    hash: 7
         
     | 
| 
      
 44 
     | 
    
         
            +
                    segments: 
         
     | 
| 
      
 45 
     | 
    
         
            +
                    - 2
         
     | 
| 
      
 46 
     | 
    
         
            +
                    - 0
         
     | 
| 
      
 47 
     | 
    
         
            +
                    - 4
         
     | 
| 
      
 48 
     | 
    
         
            +
                    version: 2.0.4
         
     | 
| 
      
 49 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 50 
     | 
    
         
            +
              version_requirements: *id002
         
     | 
| 
      
 51 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency 
         
     | 
| 
      
 52 
     | 
    
         
            +
              name: hoe
         
     | 
| 
      
 53 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 54 
     | 
    
         
            +
              requirement: &id003 !ruby/object:Gem::Requirement 
         
     | 
| 
      
 55 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 56 
     | 
    
         
            +
                requirements: 
         
     | 
| 
      
 57 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 58 
     | 
    
         
            +
                  - !ruby/object:Gem::Version 
         
     | 
| 
      
 59 
     | 
    
         
            +
                    hash: 19
         
     | 
| 
      
 60 
     | 
    
         
            +
                    segments: 
         
     | 
| 
      
 61 
     | 
    
         
            +
                    - 2
         
     | 
| 
      
 62 
     | 
    
         
            +
                    - 7
         
     | 
| 
      
 63 
     | 
    
         
            +
                    - 0
         
     | 
| 
      
 64 
     | 
    
         
            +
                    version: 2.7.0
         
     | 
| 
      
 65 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 66 
     | 
    
         
            +
              version_requirements: *id003
         
     | 
| 
      
 67 
     | 
    
         
            +
            description: |-
         
     | 
| 
      
 68 
     | 
    
         
            +
              An API for OptionsHouse (http://optionshouse.com).
         
     | 
| 
      
 69 
     | 
    
         
            +
              
         
     | 
| 
      
 70 
     | 
    
         
            +
              Currently provides a mechanism for pulling stock and option quotes.
         
     | 
| 
      
 71 
     | 
    
         
            +
            email: 
         
     | 
| 
      
 72 
     | 
    
         
            +
            - andy@tinnedfruit.org
         
     | 
| 
      
 73 
     | 
    
         
            +
            executables: 
         
     | 
| 
      
 74 
     | 
    
         
            +
            - oh
         
     | 
| 
      
 75 
     | 
    
         
            +
            extensions: []
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
            extra_rdoc_files: 
         
     | 
| 
      
 78 
     | 
    
         
            +
            - History.txt
         
     | 
| 
      
 79 
     | 
    
         
            +
            - Manifest.txt
         
     | 
| 
      
 80 
     | 
    
         
            +
            files: 
         
     | 
| 
      
 81 
     | 
    
         
            +
            - .autotest
         
     | 
| 
      
 82 
     | 
    
         
            +
            - History.txt
         
     | 
| 
      
 83 
     | 
    
         
            +
            - Manifest.txt
         
     | 
| 
      
 84 
     | 
    
         
            +
            - README.rdoc
         
     | 
| 
      
 85 
     | 
    
         
            +
            - Rakefile
         
     | 
| 
      
 86 
     | 
    
         
            +
            - bin/oh
         
     | 
| 
      
 87 
     | 
    
         
            +
            - lib/oh.rb
         
     | 
| 
      
 88 
     | 
    
         
            +
            - lib/ohbjects.rb
         
     | 
| 
      
 89 
     | 
    
         
            +
            - lib/ohbjects/account.rb
         
     | 
| 
      
 90 
     | 
    
         
            +
            - lib/ohbjects/options.rb
         
     | 
| 
      
 91 
     | 
    
         
            +
            - lib/ohbjects/quote.rb
         
     | 
| 
      
 92 
     | 
    
         
            +
            - test/test_oh.rb
         
     | 
| 
      
 93 
     | 
    
         
            +
            - test/test_ohbjects.rb
         
     | 
| 
      
 94 
     | 
    
         
            +
            - test/test_options.rb
         
     | 
| 
      
 95 
     | 
    
         
            +
            has_rdoc: true
         
     | 
| 
      
 96 
     | 
    
         
            +
            homepage: http://github.com/aasmith/oh
         
     | 
| 
      
 97 
     | 
    
         
            +
            licenses: []
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
            post_install_message: 
         
     | 
| 
      
 100 
     | 
    
         
            +
            rdoc_options: 
         
     | 
| 
      
 101 
     | 
    
         
            +
            - --main
         
     | 
| 
      
 102 
     | 
    
         
            +
            - README.rdoc
         
     | 
| 
      
 103 
     | 
    
         
            +
            require_paths: 
         
     | 
| 
      
 104 
     | 
    
         
            +
            - lib
         
     | 
| 
      
 105 
     | 
    
         
            +
            required_ruby_version: !ruby/object:Gem::Requirement 
         
     | 
| 
      
 106 
     | 
    
         
            +
              none: false
         
     | 
| 
      
 107 
     | 
    
         
            +
              requirements: 
         
     | 
| 
      
 108 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 109 
     | 
    
         
            +
                - !ruby/object:Gem::Version 
         
     | 
| 
      
 110 
     | 
    
         
            +
                  hash: 3
         
     | 
| 
      
 111 
     | 
    
         
            +
                  segments: 
         
     | 
| 
      
 112 
     | 
    
         
            +
                  - 0
         
     | 
| 
      
 113 
     | 
    
         
            +
                  version: "0"
         
     | 
| 
      
 114 
     | 
    
         
            +
            required_rubygems_version: !ruby/object:Gem::Requirement 
         
     | 
| 
      
 115 
     | 
    
         
            +
              none: false
         
     | 
| 
      
 116 
     | 
    
         
            +
              requirements: 
         
     | 
| 
      
 117 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 118 
     | 
    
         
            +
                - !ruby/object:Gem::Version 
         
     | 
| 
      
 119 
     | 
    
         
            +
                  hash: 3
         
     | 
| 
      
 120 
     | 
    
         
            +
                  segments: 
         
     | 
| 
      
 121 
     | 
    
         
            +
                  - 0
         
     | 
| 
      
 122 
     | 
    
         
            +
                  version: "0"
         
     | 
| 
      
 123 
     | 
    
         
            +
            requirements: []
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
            rubyforge_project: oh
         
     | 
| 
      
 126 
     | 
    
         
            +
            rubygems_version: 1.3.7
         
     | 
| 
      
 127 
     | 
    
         
            +
            signing_key: 
         
     | 
| 
      
 128 
     | 
    
         
            +
            specification_version: 3
         
     | 
| 
      
 129 
     | 
    
         
            +
            summary: An API for OptionsHouse (http://optionshouse.com)
         
     | 
| 
      
 130 
     | 
    
         
            +
            test_files: 
         
     | 
| 
      
 131 
     | 
    
         
            +
            - test/test_options.rb
         
     | 
| 
      
 132 
     | 
    
         
            +
            - test/test_oh.rb
         
     | 
| 
      
 133 
     | 
    
         
            +
            - test/test_ohbjects.rb
         
     |