# -*- encoding: utf-8 -*- module StreamWrapper # Modfies and injects buy_basket blob information into stream. # 1. Inserts attributes from buy_basket blob into the buy_basket observation. # 2. Adds a buy_item observation for each item in the basket. class OrderBlobSplitter def initialize (input) input = StringIO.new(input) if input.class == String # Used for testing. @stream = input @buy_item_lines = [] end def gets if @buy_item_lines.size > 0 return @buy_item_lines.shift end line = @stream.gets if line && line.include?("&o=buy_basket&") extract_buy_items(line) line = reformat_buy_basket(line) end line end def eof? @stream.eof? end private # Blob format: # RW:T|2142||1079.00|215.80|0.00|strömstad||Sweden| # RW:I|2142|MATAW09 Wanni L|Overall (98)||1079.00|1| # # See also http://www.google.com/support/googleanalytics/bin/answer.py?hl=en&answer=55528 def reformat_buy_basket(line) blob = extract_order_blob(line) order_blob = blob.match(/^RW:T\|(([^\|]*\|[^\|]*){7})\|/)[1] values = order_blob.split("|") attributes = {} [:order_id, :affiliation, :total, :tax, :shipping, :city, :state, :country].each_with_index do |name, index| attributes[name] = values[index] end insert_user_attributes(line, attributes) rescue line end def extract_buy_items(line) blob = extract_order_blob(line) blob.scan(/RW:I\|(([^\|]*\|[^\|]*){5})\|/) do |item_blob| values = item_blob[0].split("|") attributes = {} [:order_id, :sku, :title, :category, :current_price, :quantity].each_with_index do |name, index| attributes[name] = values[index] end @buy_item_lines << insert_user_attributes(line, attributes, :replace).gsub("&o=buy_basket", "&o=buy_item") end end def extract_order_blob(line) CGI::unescape(line.match(/_order_blob=([^&]+)&/)[1]) rescue "" end # 81.225.100.141 - - [23/Nov/2009:06:45:23 +0100] "GET /log.gif?_order_blob=RW%3AT%7C2142%7C%7C1079.00%7C215.80%7C0.00%7Cstr%C3%B6mstad%7C%7CSweden%7C%20%0ARW%3AI%7C2142%7CMATAW09%20Wanni%20L%7COverall%20(98)%7C%7C1079.00%7C1%7C&_order_nr=002142&a=Mozilla%2F4.0%20(compatible%3B%20MSIE%208.0%3B%20Windows%20NT%205.1%3B%20Trident%2F4.0%3B%20Mozilla%2F4.0%20(compatible%3B%20MSIE%206.0%3B%20Windows%20NT%205.1%3B%20SV1)%20%3B%20.NET%20CLR%202.0.50727%3B%20.NET%20CLR%203.0.4506.2152%3B%20.NET%20CLR%203.5.30729)&aid=jetshop&l=sv&n=microsoft%20internet%20explorer&o=buy_basket&p=win32&s=1024x600&sid=www.pixiekids.se&t=Unika%20barnkl%C3%A4der%20%26%20babykl%C3%A4der%20hos%20Pixiekids%20%E2%8E%AE%200-10%20%C3%A5r&u=https%3A%2F%2Fwww.pixiekids.se%2FOrderDetailsConfirmed.aspx&uid=1258954551578656003&x=87544&z=-60& HTTP/1.1" 200 35 "https://www.pixiekids.se/OrderDetailsConfirmed.aspx" "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)" def insert_user_attributes(line, attributes, method = :merge) encoded_attributes = [] attributes_regexp = /\?(.*?)&(?!_)/ # Add existing attributes from line. unless method == :replace encoded_attributes = line.match(attributes_regexp)[1].split("&").map { |pair| pair.split("=") } end # Add in attributes from method argument. attributes.each do |key, value| next if value.nil? || value == "" encoded_key = CGI.escape("_#{key}") encoded_value = CGI.escape(value) encoded_attributes.reject!{ |k, v| k == encoded_key } encoded_attributes << [encoded_key, encoded_value] end # Reconstruct the user attributes part of the line. attributes_string = encoded_attributes.sort.map do |key, value| "#{key}=#{value}&" end.join # Replace the old user attributes in the line with the new ones. line.sub(attributes_regexp, "?#{attributes_string}") end end end