class MingleEvents::Processors::CardData

Provides ability to lookup card data, e.g., card’s type name, for any card serving as a source for the stream of events. Implements two interfaces: the standard event processing interface, handle_events, and also, for_card_event, which returns of has of card data for the given event. See the project README for additional information on using this class in a processing pipeline.

Public Class Methods

new(mingle_access, project_identifier, custom_properties = ProjectCustomProperties.new(mingle_access, project_identifier)) click to toggle source
   # File lib/mingle_events/processors/card_data.rb
11 def initialize(mingle_access, project_identifier, custom_properties = ProjectCustomProperties.new(mingle_access, project_identifier))
12   @mingle_access = mingle_access
13   @project_identifier = project_identifier
14   @custom_properties = custom_properties
15   @card_data_by_number_and_version = nil
16 end

Public Instance Methods

for_card_event(card_event) click to toggle source

Return a hash of data for the card that sourced the passed event. The data will be for the version of the card that was created by the event and not the current state of the card. Currently supported data keys are: :number, :version, :card_type_name

   # File lib/mingle_events/processors/card_data.rb
29 def for_card_event(card_event)
30   if @card_data_by_number_and_version.nil?
31     load_bulk_card_data
32   end
33   key = data_key(card_event.card_number, card_event.version)
34   @card_data_by_number_and_version[key] ||= load_card_data_for_event(card_event)
35 end
process_events(events) click to toggle source

Capture which events are card events and might require data lookup. The actual data retrieval is lazy and will only occur as needed.

   # File lib/mingle_events/processors/card_data.rb
20 def process_events(events)
21   @card_events = events.select(&:card?)     
22   events      
23 end

Private Instance Methods

data_key(number, version) click to toggle source
   # File lib/mingle_events/processors/card_data.rb
39 def data_key(number, version)
40   "#{number}:#{version}"
41 end
load_bulk_card_data() click to toggle source
   # File lib/mingle_events/processors/card_data.rb
43       def load_bulk_card_data
44         @card_data_by_number_and_version = {}
45                 
46         card_numbers = @card_events.map(&:card_number).uniq
47         path = "/api/v2/projects/#{@project_identifier}/cards/execute_mql.xml?mql=WHERE number IN (#{card_numbers.join(',')})"
48         
49         # TODO: figure out whether it makes sense to chunk a large count of card numbers
50         # into multiple requests so that the MQL "IN" clause doesn't explode. For now, we'll
51         # just punt by logging the error and letting the individual card data load explode
52         # if there's a real problem. In most polling scenarios, this is a highly unlikely
53         # problem as there will usually be 1 or a few events.
54         begin
55           raw_xml = @mingle_access.fetch_page(URIParser.escape(path))
56         rescue
57           msg = %{
58 
59 There was an error while attempting bulk load of card data. 
60 Individual data loads for each card will still be attempted.
61 
62 Root cause: 
63 
64 #{$!.message}
65 
66 Stack Trace: 
67 
68 #{($!.backtrace || []).join("\n")}
69 
70 }
71           MingleEvents.log.info(msg)
72           return
73         end
74         doc = Xml.parse(raw_xml)
75         
76         doc.select_all('/results/result').map do |card_result|
77           card_number = card_result.inner_text('number').to_i
78           card_version = card_result.inner_text('version').to_i
79           custom_properties = {}
80           @card_data_by_number_and_version[data_key(card_number, card_version)] = {
81             :number => card_number,
82             :version => card_version,
83             :card_type_name => card_result.inner_text('card_type_name'),
84             :custom_properties => custom_properties
85           }
86           card_result.children.each do |child|
87             if child.tag_name.index("cp_") == 0
88               custom_properties[@custom_properties.property_name_for_column(child.tag_name)] = 
89                 nullable_value_from_element(child)
90             end
91           end
92         end
93       end
load_card_data_for_event(card_event) click to toggle source
    # File lib/mingle_events/processors/card_data.rb
 95 def load_card_data_for_event(card_event)
 96   begin
 97     page_xml = @mingle_access.fetch_page(card_event.card_version_resource_uri)
 98     doc = Xml.parse(page_xml)
 99     custom_properties = {}
100     result = {
101       :number => card_event.card_number,
102       :version => card_event.version,
103       :card_type_name => doc.select('/card/card_type/name').inner_text,
104       :custom_properties => custom_properties
105     }
106     doc.select_all('/card/properties/property').each do |property|
107       custom_properties[property.inner_text('name')] =
108         nullable_value_from_element(property.select('value'))
109     end
110     
111     result
112   rescue HttpError => httpError
113     raise httpError unless httpError.not_found?
114 
115   end
116 end
nullable_value_from_element(element) click to toggle source
    # File lib/mingle_events/processors/card_data.rb
118 def nullable_value_from_element(element)
119   element.attr('nil') == 'true' ? nil : element.inner_text
120 end