ib-api 972.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
 - data/.gitignore +50 -0
 - data/.rspec +3 -0
 - data/.travis.yml +7 -0
 - data/CODE_OF_CONDUCT.md +74 -0
 - data/Gemfile +16 -0
 - data/Gemfile.lock +105 -0
 - data/Guardfile +24 -0
 - data/LICENSE +674 -0
 - data/README.md +65 -0
 - data/Rakefile +11 -0
 - data/VERSION +1 -0
 - data/api.gemspec +43 -0
 - data/bin/console +95 -0
 - data/bin/console.yml +3 -0
 - data/bin/setup +8 -0
 - data/changelog.md +7 -0
 - data/example/README.md +76 -0
 - data/example/account_info +54 -0
 - data/example/account_positions +30 -0
 - data/example/account_summary +88 -0
 - data/example/cancel_orders +74 -0
 - data/example/fa_accounts +25 -0
 - data/example/fundamental_data +40 -0
 - data/example/historic_data_cli +186 -0
 - data/example/list_orders +45 -0
 - data/example/portfolio_csv +81 -0
 - data/example/scanner_data +62 -0
 - data/example/template +19 -0
 - data/example/tick_data +28 -0
 - data/lib/extensions/class-extensions.rb +87 -0
 - data/lib/ib-api.rb +7 -0
 - data/lib/ib/base.rb +103 -0
 - data/lib/ib/base_properties.rb +160 -0
 - data/lib/ib/connection.rb +450 -0
 - data/lib/ib/constants.rb +393 -0
 - data/lib/ib/errors.rb +44 -0
 - data/lib/ib/logger.rb +26 -0
 - data/lib/ib/messages.rb +99 -0
 - data/lib/ib/messages/abstract_message.rb +101 -0
 - data/lib/ib/messages/incoming.rb +251 -0
 - data/lib/ib/messages/incoming/abstract_message.rb +116 -0
 - data/lib/ib/messages/incoming/account_value.rb +78 -0
 - data/lib/ib/messages/incoming/alert.rb +34 -0
 - data/lib/ib/messages/incoming/contract_data.rb +102 -0
 - data/lib/ib/messages/incoming/delta_neutral_validation.rb +23 -0
 - data/lib/ib/messages/incoming/execution_data.rb +50 -0
 - data/lib/ib/messages/incoming/historical_data.rb +84 -0
 - data/lib/ib/messages/incoming/market_depths.rb +44 -0
 - data/lib/ib/messages/incoming/next_valid_id.rb +18 -0
 - data/lib/ib/messages/incoming/open_order.rb +277 -0
 - data/lib/ib/messages/incoming/order_status.rb +85 -0
 - data/lib/ib/messages/incoming/portfolio_value.rb +78 -0
 - data/lib/ib/messages/incoming/real_time_bar.rb +32 -0
 - data/lib/ib/messages/incoming/scanner_data.rb +54 -0
 - data/lib/ib/messages/incoming/ticks.rb +268 -0
 - data/lib/ib/messages/outgoing.rb +437 -0
 - data/lib/ib/messages/outgoing/abstract_message.rb +88 -0
 - data/lib/ib/messages/outgoing/account_requests.rb +112 -0
 - data/lib/ib/messages/outgoing/bar_requests.rb +250 -0
 - data/lib/ib/messages/outgoing/place_order.rb +209 -0
 - data/lib/ib/messages/outgoing/request_marketdata.rb +99 -0
 - data/lib/ib/messages/outgoing/request_tick_data.rb +21 -0
 - data/lib/ib/model.rb +4 -0
 - data/lib/ib/models.rb +14 -0
 - data/lib/ib/server_versions.rb +114 -0
 - data/lib/ib/socket.rb +185 -0
 - data/lib/ib/support.rb +160 -0
 - data/lib/ib/version.rb +6 -0
 - data/lib/models/ib/account.rb +85 -0
 - data/lib/models/ib/account_value.rb +33 -0
 - data/lib/models/ib/bag.rb +55 -0
 - data/lib/models/ib/bar.rb +31 -0
 - data/lib/models/ib/combo_leg.rb +105 -0
 - data/lib/models/ib/condition.rb +245 -0
 - data/lib/models/ib/contract.rb +415 -0
 - data/lib/models/ib/contract_detail.rb +108 -0
 - data/lib/models/ib/execution.rb +67 -0
 - data/lib/models/ib/forex.rb +13 -0
 - data/lib/models/ib/future.rb +15 -0
 - data/lib/models/ib/index.rb +15 -0
 - data/lib/models/ib/option.rb +78 -0
 - data/lib/models/ib/option_detail.rb +55 -0
 - data/lib/models/ib/order.rb +519 -0
 - data/lib/models/ib/order_state.rb +152 -0
 - data/lib/models/ib/portfolio_value.rb +64 -0
 - data/lib/models/ib/stock.rb +16 -0
 - data/lib/models/ib/underlying.rb +34 -0
 - data/lib/models/ib/vertical.rb +96 -0
 - data/lib/requires.rb +12 -0
 - metadata +203 -0
 
| 
         @@ -0,0 +1,62 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
            #  This Script needs testing.  remove this line after sucessfully using the scanner facility
         
     | 
| 
      
 3 
     | 
    
         
            +
            #
         
     | 
| 
      
 4 
     | 
    
         
            +
            # This script setups a scan request and retreives the results.
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            require 'rubygems'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'bundler/setup'
         
     | 
| 
      
 8 
     | 
    
         
            +
            require 'ib-api'
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            # Connect to IB TWS.
         
     | 
| 
      
 13 
     | 
    
         
            +
            ib = IB::Connection.new :client_id => 1112 , port: 7496 do | gw | #, :port => 7497 # TWS
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            	# Subscribe to TWS alerts/errors
         
     | 
| 
      
 17 
     | 
    
         
            +
            	gw.subscribe(:Alert) { |msg| puts msg.to_human }
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            	# Subscribe to ScannerData incoming events.  The code passed in the block
         
     | 
| 
      
 21 
     | 
    
         
            +
            	# will be executed when a message of that type is received, with the received
         
     | 
| 
      
 22 
     | 
    
         
            +
            	# message as its argument. In this case, we just print out the data.
         
     | 
| 
      
 23 
     | 
    
         
            +
            	#
         
     | 
| 
      
 24 
     | 
    
         
            +
            	gw.subscribe(IB::Messages::Incoming::ScannerData) do |msg|
         
     | 
| 
      
 25 
     | 
    
         
            +
            		puts "ID: #{msg.request_id} : #{msg.count} items:"
         
     | 
| 
      
 26 
     | 
    
         
            +
            		msg.results.each { |entry| puts "  #{entry}" }
         
     | 
| 
      
 27 
     | 
    
         
            +
            	end
         
     | 
| 
      
 28 
     | 
    
         
            +
            end
         
     | 
| 
      
 29 
     | 
    
         
            +
            id = 42
         
     | 
| 
      
 30 
     | 
    
         
            +
            # Now we actually request scanner data for the type of scan we are interested.
         
     | 
| 
      
 31 
     | 
    
         
            +
            # Some scan types can be found here:  http://www.interactivebrokers.com/php/apiUsersGuide/apiguide/tables/available_market_scanners.htm
         
     | 
| 
      
 32 
     | 
    
         
            +
            mess = IB::Messages::Outgoing::RequestScannerSubscription.new(
         
     | 
| 
      
 33 
     | 
    
         
            +
                                :request_id => id,
         
     | 
| 
      
 34 
     | 
    
         
            +
                                :number_of_rows => 20,
         
     | 
| 
      
 35 
     | 
    
         
            +
                                :instrument => "STK",
         
     | 
| 
      
 36 
     | 
    
         
            +
                                :location_code => 'STK.US.MAJOR',
         
     | 
| 
      
 37 
     | 
    
         
            +
                                :scan_code  => 'TOP_PERC_GAIN',
         
     | 
| 
      
 38 
     | 
    
         
            +
                                :above_price => 4.0,
         
     | 
| 
      
 39 
     | 
    
         
            +
                                :below_price => Float::MAX,
         
     | 
| 
      
 40 
     | 
    
         
            +
                                :above_volume => 5000,
         
     | 
| 
      
 41 
     | 
    
         
            +
                                :market_cap_above => 100000000,
         
     | 
| 
      
 42 
     | 
    
         
            +
                                :market_cap_below =>  Float::MAX,
         
     | 
| 
      
 43 
     | 
    
         
            +
                                :moody_rating_above => "",
         
     | 
| 
      
 44 
     | 
    
         
            +
                                :moody_rating_below => "",
         
     | 
| 
      
 45 
     | 
    
         
            +
                                :sp_rating_above => "",
         
     | 
| 
      
 46 
     | 
    
         
            +
                                :sp_rating_below => "",
         
     | 
| 
      
 47 
     | 
    
         
            +
                                :maturity_date_above => "",
         
     | 
| 
      
 48 
     | 
    
         
            +
                                :maturity_date_below => "",
         
     | 
| 
      
 49 
     | 
    
         
            +
                                :coupon_rate_above => Float::MAX,
         
     | 
| 
      
 50 
     | 
    
         
            +
                                :coupon_rate_below => Float::MAX,
         
     | 
| 
      
 51 
     | 
    
         
            +
                                :exclude_convertible => "",
         
     | 
| 
      
 52 
     | 
    
         
            +
                                :average_option_volume_above => "", # ?
         
     | 
| 
      
 53 
     | 
    
         
            +
                                :scanner_setting_pairs => "Annual,true",
         
     | 
| 
      
 54 
     | 
    
         
            +
                                :stock_type_filter => "Stock"
         
     | 
| 
      
 55 
     | 
    
         
            +
                                )
         
     | 
| 
      
 56 
     | 
    
         
            +
                                
         
     | 
| 
      
 57 
     | 
    
         
            +
            ib.send_message( mess )   
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
            # IB does not send any indication when all  data is done being delivered.
         
     | 
| 
      
 60 
     | 
    
         
            +
            # So we need to interrupt manually when our request is answered.
         
     | 
| 
      
 61 
     | 
    
         
            +
            puts "\n******** Press <Enter> to exit... *********\n\n"
         
     | 
| 
      
 62 
     | 
    
         
            +
            STDIN.gets
         
     | 
    
        data/example/template
    ADDED
    
    | 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
            #
         
     | 
| 
      
 3 
     | 
    
         
            +
            # Your script description here...
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            require 'bundler/setup'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'ib-api'
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            # First, connect to IB TWS and subscribe for events. 
         
     | 
| 
      
 9 
     | 
    
         
            +
            ib = IB::Connection.new :client_id => 1112  do | gw | #, :port => 7497 # TWS
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            	# Subscribe to TWS alerts/errors
         
     | 
| 
      
 12 
     | 
    
         
            +
            	gw.subscribe(:Alert, :ManagedAccounts) { |msg| puts msg.to_human }
         
     | 
| 
      
 13 
     | 
    
         
            +
            	# Set log level
         
     | 
| 
      
 14 
     | 
    
         
            +
            	gw.logger.level = Logger::FATAL # DEBUG  -- INFO -- WARN -- ERROR  -- FATAL
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            end
         
     | 
| 
      
 17 
     | 
    
         
            +
            # Put your code here
         
     | 
| 
      
 18 
     | 
    
         
            +
            # ...
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
    
        data/example/tick_data
    ADDED
    
    | 
         @@ -0,0 +1,28 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # This script reproduces https://github.com/ib-ruby/ib-ruby/issues/12
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            require 'bundler/setup'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'ib-api'
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            contract = IB::Stock.new :symbol=> 'AAPL'
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            # First, connect to IB Gateway.
         
     | 
| 
      
 11 
     | 
    
         
            +
            ib = IB::Connection.new :client_id => 1112 # id to identify your script
         
     | 
| 
      
 12 
     | 
    
         
            +
            #                       :port => 7497     # TWS connection (instead of default Gateway)
         
     | 
| 
      
 13 
     | 
    
         
            +
            #                        :received => false  # Do not keep all received messages in memory
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            ib.subscribe(:Alert) { |msg| puts msg.to_human }
         
     | 
| 
      
 16 
     | 
    
         
            +
            ib.subscribe(:MarketDataType) { |msg| puts msg.to_human }
         
     | 
| 
      
 17 
     | 
    
         
            +
            ib.subscribe(:TickGeneric, :TickString, :TickPrice, :TickSize) { |msg| puts msg.inspect }
         
     | 
| 
      
 18 
     | 
    
         
            +
            ib.send_message :RequestMarketDataType, :market_data_type => :delayed
         
     | 
| 
      
 19 
     | 
    
         
            +
            ib.send_message :RequestMarketData, id: 123, contract:  contract
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            puts "\nSubscribed to market data"
         
     | 
| 
      
 22 
     | 
    
         
            +
            puts "\n******** Press <Enter> to cancel... *********\n\n"
         
     | 
| 
      
 23 
     | 
    
         
            +
            gets
         
     | 
| 
      
 24 
     | 
    
         
            +
            puts "Cancelling market data subscription.."
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            ib.send_message :CancelMarketData, id: 123
         
     | 
| 
      
 27 
     | 
    
         
            +
            sleep 1
         
     | 
| 
      
 28 
     | 
    
         
            +
            puts "Done."
         
     | 
| 
         @@ -0,0 +1,87 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module CoreExtensions
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Array
         
     | 
| 
      
 3 
     | 
    
         
            +
                module DuplicatesCounter
         
     | 
| 
      
 4 
     | 
    
         
            +
                  def count_duplicates
         
     | 
| 
      
 5 
     | 
    
         
            +
                    self.each_with_object(Hash.new(0)) { |element, counter| counter[element] += 1 }.sort_by{|k,v| -v}.to_h
         
     | 
| 
      
 6 
     | 
    
         
            +
                  end
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
            end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            Array.include CoreExtensions::Array::DuplicatesCounter
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            class Time
         
     | 
| 
      
 17 
     | 
    
         
            +
              # Render datetime in IB format (zero padded "yyyymmdd HH:mm:ss")
         
     | 
| 
      
 18 
     | 
    
         
            +
              def to_ib
         
     | 
| 
      
 19 
     | 
    
         
            +
                "#{year}#{sprintf("%02d", month)}#{sprintf("%02d", day)} " +
         
     | 
| 
      
 20 
     | 
    
         
            +
                    "#{sprintf("%02d", hour)}:#{sprintf("%02d", min)}:#{sprintf("%02d", sec)}"
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
            end # Time
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            class Numeric
         
     | 
| 
      
 25 
     | 
    
         
            +
              # Conversion 0/1 into true/false
         
     | 
| 
      
 26 
     | 
    
         
            +
              def to_bool
         
     | 
| 
      
 27 
     | 
    
         
            +
                self == 0 ? false : true
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
            end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            class TrueClass
         
     | 
| 
      
 32 
     | 
    
         
            +
              def to_bool
         
     | 
| 
      
 33 
     | 
    
         
            +
                self
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
            end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            class FalseClass
         
     | 
| 
      
 38 
     | 
    
         
            +
              def to_bool
         
     | 
| 
      
 39 
     | 
    
         
            +
                self
         
     | 
| 
      
 40 
     | 
    
         
            +
              end
         
     | 
| 
      
 41 
     | 
    
         
            +
            end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
            class String
         
     | 
| 
      
 44 
     | 
    
         
            +
              def to_bool
         
     | 
| 
      
 45 
     | 
    
         
            +
                case self.chomp.upcase
         
     | 
| 
      
 46 
     | 
    
         
            +
                  when 'TRUE', 'T', '1'
         
     | 
| 
      
 47 
     | 
    
         
            +
                    true
         
     | 
| 
      
 48 
     | 
    
         
            +
                  when 'FALSE', 'F', '0', ''
         
     | 
| 
      
 49 
     | 
    
         
            +
                    false
         
     | 
| 
      
 50 
     | 
    
         
            +
                  else
         
     | 
| 
      
 51 
     | 
    
         
            +
                    error "Unable to convert #{self} to bool"
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
              end
         
     | 
| 
      
 54 
     | 
    
         
            +
            end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
            class NilClass
         
     | 
| 
      
 57 
     | 
    
         
            +
              # We still need to pass on nil, meaning: no value
         
     | 
| 
      
 58 
     | 
    
         
            +
              def to_bool
         
     | 
| 
      
 59 
     | 
    
         
            +
                self
         
     | 
| 
      
 60 
     | 
    
         
            +
              end
         
     | 
| 
      
 61 
     | 
    
         
            +
            end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
            class Symbol
         
     | 
| 
      
 64 
     | 
    
         
            +
              def to_f
         
     | 
| 
      
 65 
     | 
    
         
            +
                0
         
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
              # ActiveModel serialization depends on this method
         
     | 
| 
      
 69 
     | 
    
         
            +
              def <=> other
         
     | 
| 
      
 70 
     | 
    
         
            +
                to_s <=> other.to_s
         
     | 
| 
      
 71 
     | 
    
         
            +
              end
         
     | 
| 
      
 72 
     | 
    
         
            +
            end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
            class Object
         
     | 
| 
      
 75 
     | 
    
         
            +
              # We still need to pass on nil, meaning: no value
         
     | 
| 
      
 76 
     | 
    
         
            +
              def to_sup
         
     | 
| 
      
 77 
     | 
    
         
            +
                self.to_s.upcase unless self.nil?
         
     | 
| 
      
 78 
     | 
    
         
            +
              end
         
     | 
| 
      
 79 
     | 
    
         
            +
            end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
            ### Patching Object#error in ib/errors
         
     | 
| 
      
 82 
     | 
    
         
            +
            #  def error message, type=:standard
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
            ### Patching Object#log, #default_logger= in ib/logger
         
     | 
| 
      
 85 
     | 
    
         
            +
            #  def default_logger
         
     | 
| 
      
 86 
     | 
    
         
            +
            #  def default_logger= logger
         
     | 
| 
      
 87 
     | 
    
         
            +
            #  def log *args
         
     | 
    
        data/lib/ib-api.rb
    ADDED
    
    
    
        data/lib/ib/base.rb
    ADDED
    
    | 
         @@ -0,0 +1,103 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module IB
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
              # Base class for tableless IB data Models, extends ActiveModel API
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Base
         
     | 
| 
      
 5 
     | 
    
         
            +
                extend ActiveModel::Naming
         
     | 
| 
      
 6 
     | 
    
         
            +
                extend ActiveModel::Callbacks
         
     | 
| 
      
 7 
     | 
    
         
            +
                include ActiveModel::Validations
         
     | 
| 
      
 8 
     | 
    
         
            +
                include ActiveModel::Serialization
         
     | 
| 
      
 9 
     | 
    
         
            +
                #include ActiveModel::Serializers::Xml
         
     | 
| 
      
 10 
     | 
    
         
            +
                include ActiveModel::Serializers::JSON
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                define_model_callbacks :initialize
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                # If a opts hash is given, keys are taken as attribute names, values as data.
         
     | 
| 
      
 15 
     | 
    
         
            +
                # The model instance fields are then set automatically from the opts Hash.
         
     | 
| 
      
 16 
     | 
    
         
            +
                def initialize attributes={}, opts={}
         
     | 
| 
      
 17 
     | 
    
         
            +
                  run_callbacks :initialize do
         
     | 
| 
      
 18 
     | 
    
         
            +
                    error "Argument must be a Hash", :args unless attributes.is_a?(Hash)
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                    self.attributes = attributes # set_attribute_defaults is now after_init callback
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                # ActiveModel API (for serialization)
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                def attributes
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @attributes ||= Hash.new #HashWithIndifferentAccess.new
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                def attributes= attrs
         
     | 
| 
      
 31 
     | 
    
         
            +
                  attrs.keys.each { |key| self.send("#{key}=", attrs[key]) }
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                # ActiveModel-style read/write_attribute accessors
         
     | 
| 
      
 35 
     | 
    
         
            +
                def [] key
         
     | 
| 
      
 36 
     | 
    
         
            +
                  attributes[key.to_sym]
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                def update_attribute key, value
         
     | 
| 
      
 40 
     | 
    
         
            +
                  @attributes[key.to_sym] = value
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                def []= key, val
         
     | 
| 
      
 44 
     | 
    
         
            +
                  # p key, val
         
     | 
| 
      
 45 
     | 
    
         
            +
                  attributes[key.to_sym] = val
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                def to_model
         
     | 
| 
      
 49 
     | 
    
         
            +
                  self
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                def new_record?
         
     | 
| 
      
 53 
     | 
    
         
            +
                  true
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                def save
         
     | 
| 
      
 57 
     | 
    
         
            +
                  valid?
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                alias save! save
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                ### Noop methods mocking ActiveRecord::Base macros
         
     | 
| 
      
 63 
     | 
    
         
            +
                
         
     | 
| 
      
 64 
     | 
    
         
            +
                def self.attr_protected *args
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                def self.attr_accessible *args
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                ### ActiveRecord::Base association API mocks
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                def self.belongs_to model, *args
         
     | 
| 
      
 73 
     | 
    
         
            +
                  attr_accessor model
         
     | 
| 
      
 74 
     | 
    
         
            +
                end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                def self.has_one model, *args
         
     | 
| 
      
 77 
     | 
    
         
            +
                  attr_accessor model
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                def self.has_many models, *args
         
     | 
| 
      
 81 
     | 
    
         
            +
                  attr_accessor models
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                  define_method(models) do
         
     | 
| 
      
 84 
     | 
    
         
            +
                    self.instance_variable_get("@#{models}") ||
         
     | 
| 
      
 85 
     | 
    
         
            +
                      self.instance_variable_set("@#{models}", [])
         
     | 
| 
      
 86 
     | 
    
         
            +
                  end
         
     | 
| 
      
 87 
     | 
    
         
            +
                end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                def self.find *args
         
     | 
| 
      
 90 
     | 
    
         
            +
                  []
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                ### ActiveRecord::Base callback API mocks
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                define_model_callbacks :initialize, :only => :after
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                ### ActiveRecord::Base misc
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                def self.serialize *properties
         
     | 
| 
      
 100 
     | 
    
         
            +
                end
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
              end # Model
         
     | 
| 
      
 103 
     | 
    
         
            +
            end # module IB
         
     | 
| 
         @@ -0,0 +1,160 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'active_model'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'active_support/concern'
         
     | 
| 
      
 3 
     | 
    
         
            +
            #require 'active_support/hash_with_indifferent_access'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module IB
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              # Module adds prop Macro and
         
     | 
| 
      
 8 
     | 
    
         
            +
              module BaseProperties
         
     | 
| 
      
 9 
     | 
    
         
            +
                extend ActiveSupport::Concern
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                ### Instance methods
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                # Default presentation
         
     | 
| 
      
 14 
     | 
    
         
            +
                def to_human
         
     | 
| 
      
 15 
     | 
    
         
            +
                  "<#{self.class.to_s.demodulize}: " + attributes.map do |attr, value|
         
     | 
| 
      
 16 
     | 
    
         
            +
                    "#{attr}: #{value}" unless value.nil?
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end.compact.sort.join(' ') + ">"
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                # Comparison support
         
     | 
| 
      
 21 
     | 
    
         
            +
                def content_attributes
         
     | 
| 
      
 22 
     | 
    
         
            +
                  #HashWithIndifferentAccess[attributes.reject do |(attr, _)|
         
     | 
| 
      
 23 
     | 
    
         
            +
            			#NoMethodError if a Hash is assigned to an attribute
         
     | 
| 
      
 24 
     | 
    
         
            +
                  Hash[attributes.reject do |(attr, _)|
         
     | 
| 
      
 25 
     | 
    
         
            +
                                              attr.to_s =~ /(_count)\z/ ||
         
     | 
| 
      
 26 
     | 
    
         
            +
                                                [:created_at, :updated_at, :type,
         
     | 
| 
      
 27 
     | 
    
         
            +
                                                 :id, :order_id, :contract_id].include?(attr.to_sym)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end]
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            =begin
         
     | 
| 
      
 32 
     | 
    
         
            +
            Remove all Time-Stamps from the list of Attributes
         
     | 
| 
      
 33 
     | 
    
         
            +
            =end
         
     | 
| 
      
 34 
     | 
    
         
            +
            		def invariant_attributes
         
     | 
| 
      
 35 
     | 
    
         
            +
            				attributes.reject{|x| x =~ /_at/}
         
     | 
| 
      
 36 
     | 
    
         
            +
            		end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                # Update nil attributes from given Hash or model
         
     | 
| 
      
 39 
     | 
    
         
            +
                def update_missing attrs
         
     | 
| 
      
 40 
     | 
    
         
            +
                  attrs = attrs.content_attributes unless attrs.kind_of?(Hash)
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  attrs.each { |attr, val| send "#{attr}=", val if send(attr).blank? }
         
     | 
| 
      
 43 
     | 
    
         
            +
                  self # for chaining
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                # Default Model comparison
         
     | 
| 
      
 47 
     | 
    
         
            +
                def == other
         
     | 
| 
      
 48 
     | 
    
         
            +
                  case other
         
     | 
| 
      
 49 
     | 
    
         
            +
                  when String # Probably a Rails URI, delegate to AR::Base
         
     | 
| 
      
 50 
     | 
    
         
            +
                    super(other)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  else
         
     | 
| 
      
 52 
     | 
    
         
            +
                    content_attributes.keys.inject(true) { |res, key|
         
     | 
| 
      
 53 
     | 
    
         
            +
                      res && other.respond_to?(key) && (send(key) == other.send(key)) }
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                ### Default attributes support
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                def default_attributes
         
     | 
| 
      
 60 
     | 
    
         
            +
                  {:created_at => Time.now
         
     | 
| 
      
 61 
     | 
    
         
            +
                 #  :updated_at => Time.now,
         
     | 
| 
      
 62 
     | 
    
         
            +
                   }
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                def set_attribute_defaults
         
     | 
| 
      
 66 
     | 
    
         
            +
                  default_attributes.each do |key, val|
         
     | 
| 
      
 67 
     | 
    
         
            +
                    self.send("#{key}=", val) if self.send(key).nil?
         
     | 
| 
      
 68 
     | 
    
         
            +
                    # self.send("#{key}=", val) if self[key].nil? # Problems with association defaults
         
     | 
| 
      
 69 
     | 
    
         
            +
                  end
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                included do
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                  after_initialize :set_attribute_defaults
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                  ### Class macros
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                  def self.prop *properties
         
     | 
| 
      
 79 
     | 
    
         
            +
                    prop_hash = properties.last.is_a?(Hash) ? properties.pop : {}
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                    properties.each { |names| define_property names, nil }
         
     | 
| 
      
 82 
     | 
    
         
            +
                    prop_hash.each { |names, type| define_property names, type }
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                  def self.define_property names, body
         
     | 
| 
      
 86 
     | 
    
         
            +
                    aliases = [names].flatten
         
     | 
| 
      
 87 
     | 
    
         
            +
                    name = aliases.shift
         
     | 
| 
      
 88 
     | 
    
         
            +
                    instance_eval do
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                      define_property_methods name, body
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                      aliases.each do |ali|
         
     | 
| 
      
 93 
     | 
    
         
            +
                        alias_method "#{ali}", name
         
     | 
| 
      
 94 
     | 
    
         
            +
                        alias_method "#{ali}=", "#{name}="
         
     | 
| 
      
 95 
     | 
    
         
            +
                      end
         
     | 
| 
      
 96 
     | 
    
         
            +
                    end
         
     | 
| 
      
 97 
     | 
    
         
            +
                  end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                  def self.define_property_methods name, body={}
         
     | 
| 
      
 100 
     | 
    
         
            +
                    #p name, body
         
     | 
| 
      
 101 
     | 
    
         
            +
                    case body
         
     | 
| 
      
 102 
     | 
    
         
            +
                    when '' # default getter and setter
         
     | 
| 
      
 103 
     | 
    
         
            +
                      define_property_methods name
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                    when Array # [setter, getter, validators]
         
     | 
| 
      
 106 
     | 
    
         
            +
                      define_property_methods name,
         
     | 
| 
      
 107 
     | 
    
         
            +
                        :get => body[0],
         
     | 
| 
      
 108 
     | 
    
         
            +
                        :set => body[1],
         
     | 
| 
      
 109 
     | 
    
         
            +
                        :validate => body[2]
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                    when Hash # recursion base case
         
     | 
| 
      
 112 
     | 
    
         
            +
                      getter = case # Define getter
         
     | 
| 
      
 113 
     | 
    
         
            +
                      when body[:get].respond_to?(:call)
         
     | 
| 
      
 114 
     | 
    
         
            +
                        body[:get]
         
     | 
| 
      
 115 
     | 
    
         
            +
                      when body[:get]
         
     | 
| 
      
 116 
     | 
    
         
            +
                        proc { self[name].send "to_#{body[:get]}" }
         
     | 
| 
      
 117 
     | 
    
         
            +
                      when VALUES[name] # property is encoded
         
     | 
| 
      
 118 
     | 
    
         
            +
                        proc { VALUES[name][self[name]] }
         
     | 
| 
      
 119 
     | 
    
         
            +
                      else
         
     | 
| 
      
 120 
     | 
    
         
            +
                        proc { self[name] }
         
     | 
| 
      
 121 
     | 
    
         
            +
                      end
         
     | 
| 
      
 122 
     | 
    
         
            +
                      define_method name, &getter if getter
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                      setter = case # Define setter
         
     | 
| 
      
 125 
     | 
    
         
            +
                      when body[:set].respond_to?(:call)
         
     | 
| 
      
 126 
     | 
    
         
            +
                        body[:set]
         
     | 
| 
      
 127 
     | 
    
         
            +
                      when body[:set]
         
     | 
| 
      
 128 
     | 
    
         
            +
                        proc { |value| self[name] = value.send "to_#{body[:set]}" }
         
     | 
| 
      
 129 
     | 
    
         
            +
                      when CODES[name] # property is encoded
         
     | 
| 
      
 130 
     | 
    
         
            +
                        proc { |value| self[name] = CODES[name][value] || value }
         
     | 
| 
      
 131 
     | 
    
         
            +
                      else
         
     | 
| 
      
 132 
     | 
    
         
            +
                        proc { |value| self[name] = value } # p name, value;
         
     | 
| 
      
 133 
     | 
    
         
            +
                      end
         
     | 
| 
      
 134 
     | 
    
         
            +
                      define_method "#{name}=", &setter if setter
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                      # Define validator(s)
         
     | 
| 
      
 137 
     | 
    
         
            +
                      [body[:validate]].flatten.compact.each do |validator|
         
     | 
| 
      
 138 
     | 
    
         
            +
                        case validator
         
     | 
| 
      
 139 
     | 
    
         
            +
                        when Proc
         
     | 
| 
      
 140 
     | 
    
         
            +
                          validates_each name, &validator
         
     | 
| 
      
 141 
     | 
    
         
            +
                        when Hash
         
     | 
| 
      
 142 
     | 
    
         
            +
                          validates name, validator.dup
         
     | 
| 
      
 143 
     | 
    
         
            +
                        end
         
     | 
| 
      
 144 
     | 
    
         
            +
                      end
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
                      # TODO define self[:name] accessors for :virtual and :flag properties
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
                    else # setter given
         
     | 
| 
      
 149 
     | 
    
         
            +
                      define_property_methods name, :set => body, :get => body
         
     | 
| 
      
 150 
     | 
    
         
            +
                    end
         
     | 
| 
      
 151 
     | 
    
         
            +
                  end
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
                  # Timestamps in lightweight models
         
     | 
| 
      
 154 
     | 
    
         
            +
                  unless defined?(ActiveRecord::Base) && ancestors.include?(ActiveRecord::Base)
         
     | 
| 
      
 155 
     | 
    
         
            +
                    prop :created_at #, :updated_at
         
     | 
| 
      
 156 
     | 
    
         
            +
                  end
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                end # included
         
     | 
| 
      
 159 
     | 
    
         
            +
              end # module BaseProperties
         
     | 
| 
      
 160 
     | 
    
         
            +
            end
         
     |