Module: Amigo::Job::InstanceMethods

Defined in:
lib/amigo/job.rb

Instance Method Summary collapse

Instance Method Details

#changed(field, values = {}) ⇒ Object

Create a matcher against the ‘changed’ hash of an update event.

Example:

on 'myapp.customer.update' do |payload, _|
    customerid, changes, _ = *payload
    customer = MyApp::Customer[ customerid ] or return false

    case changes
    when changed( :password )
        send_password_change_email( customer )
    when changed( :activation_code, to: nil )
        send_welcome_email( customer )
    when changed( :type, from: 'seeder', to: 'eater' )
        send_becoming_an_eater_email( customer )
    end
end


75
76
77
78
79
80
81
82
# File 'lib/amigo/job.rb', line 75

def changed(field, values={})
  unless @change_matchers[[field, values]]
    match_proc = self.make_match_proc_for(field, values)
    @change_matchers[[field, values]] = match_proc
  end

  return @change_matchers[[field, values]]
end

#changed_at(field, index, values = {}) ⇒ Object

Create a matcher against a changed Hash JSON column of an update event.

Example:

on 'myapp.customer.update' do |payload, _|
    customerid, changes, _ = *payload

    case changes
    when changed_at( :flags, :trustworthy, to: true )
        mark_customer_safe_in_external_service( customerid )
    end
end


192
193
194
# File 'lib/amigo/job.rb', line 192

def changed_at(field, index, values={})
  return self.method(:match_change_at).to_proc.curry[field, index, values]
end

#initializeObject



17
18
19
20
# File 'lib/amigo/job.rb', line 17

def initialize(*)
  super
  @change_matchers = {}
end

#loggerObject



22
23
24
# File 'lib/amigo/job.rb', line 22

def logger
  return Sidekiq.logger
end

#lookup_model(klass, payload_or_id) ⇒ Object

Return klass[payload_or_id] if it exists, or raise an error if it does not. payload_or_id can be an integer, or the async job payload if the id is the first argument.

Examples:

  • ‘customer = self.lookup_model( MyApp::Customer, event )`

  • ‘customer = self.lookup_model( MyApp::Customer, event.payload )`

  • ‘customer = self.lookup_model( MyApp::Customer, customer_id )`



45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/amigo/job.rb', line 45

def lookup_model(klass, payload_or_id)
  if payload_or_id.is_a?(Integer)
    id = payload_or_id
  elsif payload_or_id.respond_to?(:payload)
    id = payload_or_id.payload.first
  elsif payload_or_id.respond_to?(:first)
    id = payload_or_id.first
  else
    raise "Don't know how to handle #{payload_or_id}"
  end
  result = klass[id] or raise "%s[%p] does not exist" % [klass.name, id]
  return result
end

#make_match_proc_for(field, values) ⇒ Object

Make a curried Proc for calling a match method with the given field and values.



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/amigo/job.rb', line 85

def make_match_proc_for(field, values)
  return self.method(:match_any_change).to_proc.curry[field] if values.empty?

  if values.key?(:from)
    if values.key?(:to)
      return self.method(:match_change_from_to).to_proc.
          curry[ field, values[:from], values[:to] ]
    else
      return self.method(:match_change_from).to_proc.
          curry[ field, values[:from] ]
    end
  elsif values.key?(:to)
    return self.method(:match_change_to).to_proc.
        curry[ field, values[:to] ]
  else
    raise ScriptError,
          "Unhandled change option/s: %p; expected :to and/or :from" % [values.keys]
  end
end

#match_any_change(field, changes) ⇒ Object

Returns true if the given field is listed at all in the specified changes.



107
108
109
110
# File 'lib/amigo/job.rb', line 107

def match_any_change(field, changes)
  self.logger.debug "Checking for existance of field %p in %p" % [field, changes]
  return changes&.key?(field.to_s)
end

#match_change_at(field, index, values, changes) ⇒ Object

Return true if ‘field` has changed in the specified changes, configured by values (contains from, to, or whatever supported kwargs). Unlike other matches, changes is expected to be an entire Hash and may contain a value at index in either or both sets of changes. This method does not attempt to do the matching itself. Rather, it sets up the data to defer to the other matcher methods.



202
203
204
205
206
207
208
209
# File 'lib/amigo/job.rb', line 202

def match_change_at(field, index, values, changes)
  field = field.to_s
  return false unless changes&.key?(field)
  index = index.to_s
  old_values, new_values = changes[field]
  unrolled_changes = self.unroll_hash_changes(old_values, new_values)
  return self.changed(index, values)[unrolled_changes]
end

#match_change_from(field, from, changes) ⇒ Object

Returns true if the given field is listed in the specified changes, and the value it changed from matches from. The from value can be:

a Regexp

The old value is stringified, and matched against the from Regexp.

an immediate value (String, Numeric, NilClass, etc.)

The old value is matched using Object#==.



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/amigo/job.rb', line 146

def match_change_from(field, from, changes)
  self.logger.debug "Checking for change from %p of field %p in %p" % [from, field, changes]
  return false unless changes&.key?(field.to_s)

  oldval = changes[field.to_s][0]

  case from
    when NilClass, Numeric, String, TrueClass, FalseClass
      return oldval == from
    when Regexp
      return from.match(oldval)
    when Proc
      return from[oldval]
    else
      raise TypeError, "Unhandled type of 'from' criteria %p (a %p)" % [from, from.class]
  end
end

#match_change_from_to(field, from, to, changes) ⇒ Object

Returns true if the given field is listed in the specified changes, and the value it changed from matches from and the value it changed to matches to. The from and to values can be:

a Regexp

The corresponding value is stringified, and matched against the Regexp.

an immediate value (String, Numeric, NilClass, etc.)

The corresponding value is matched using Object#==.



173
174
175
176
177
178
179
# File 'lib/amigo/job.rb', line 173

def match_change_from_to(field, from, to, changes)
  self.logger.debug "Checking for change from %p to %p of field %p in %p" %
    [from, to, field, changes]
  return false unless changes&.key?(field.to_s)
  return self.match_change_to(field, to, changes) &&
      self.match_change_from(field, from, changes)
end

#match_change_to(field, to, changes) ⇒ Object

Returns true if the given field is listed in the specified changes, and the value it changed to matches to. The to value can be:

a Regexp

The new value is stringified, and matched against the to Regexp.

an immediate value (String, Numeric, NilClass, etc.)

The new value is matched using Object#==.



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/amigo/job.rb', line 120

def match_change_to(field, to, changes)
  self.logger.debug "Checking for change to %p of field %p in %p" % [to, field, changes]
  return false unless changes&.key?(field.to_s)

  newval = changes[field.to_s][1]

  case to
    when NilClass, Numeric, String, TrueClass, FalseClass
      return newval == to
    when Regexp
      return to.match(newval)
    when Proc
      return to[newval]
    else
      raise TypeError, "Unhandled type of 'to' criteria %p (a %p)" % [to, to.class]
  end
end

#perform(*args) ⇒ Object



26
27
28
29
30
31
32
33
34
35
# File 'lib/amigo/job.rb', line 26

def perform(*args)
  if args.empty?
    event = nil
  elsif args.size == 1
    event = Amigo::Event.from_json(args[0])
  else
    raise "perform should always be called with no args or [Amigo::Event#as_json], got %p" % [args]
  end
  self._perform(event)
end

#unroll_hash_changes(old_hash, new_hash) ⇒ Object

Given two hashes that represent the before and after column hash values, return a hash in the usual “changes” format, where keys are the hash keys with changed values, and values are a tuple of the before and after values.



215
216
217
218
219
220
221
222
223
# File 'lib/amigo/job.rb', line 215

def unroll_hash_changes(old_hash, new_hash)
  old_hash ||= {}
  new_hash ||= {}
  return old_hash.keys.concat(new_hash.keys).uniq.each_with_object({}) do |key, h|
    old_val = old_hash[key]
    new_val = new_hash[key]
    h[key] = [old_val, new_val] if old_val != new_val
  end
end