Skip to content

Commit

Permalink
Add a new option include_sort_info
Browse files Browse the repository at this point in the history
If enabled, all events created by the Agent will have a `sort_info` key
whose value is a hash containing the keys `position` and `count`.

This overrides #1768.
  • Loading branch information
knu committed Nov 2, 2016
1 parent 50123dc commit c50e2f5
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 12 deletions.
39 changes: 33 additions & 6 deletions app/concerns/sortable_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module SortableEvents
end

EVENTS_ORDER_KEY = 'events_order'.freeze
EVENTS_DESCRIPTION = 'events created in each run'.freeze

def description_events_order(*args)
self.class.description_events_order(*args)
Expand All @@ -25,7 +26,7 @@ def cannot_order_created_events?
!can_order_created_events?
end

def description_events_order(events = 'events created in each run', events_order_key = EVENTS_ORDER_KEY)
def description_events_order(events = EVENTS_DESCRIPTION, events_order_key = EVENTS_ORDER_KEY)
<<-MD.lstrip
To specify the order of #{events}, set `#{events_order_key}` to an array of sort keys, each of which looks like either `expression` or `[expression, type, descending]`, as described as follows:
Expand All @@ -38,6 +39,17 @@ def description_events_order(events = 'events created in each run', events_order
Sort keys listed earlier take precedence over ones listed later. For example, if you want to sort articles by the date and then by the author, specify `[["{{date}}", "time"], "{{author}}"]`.
Sorting is done stably, so even if all events have the same set of sort key values the original order is retained. Also, a special Liquid variable `_index_` is provided, which contains the zero-based index number of each event, which means you can exactly reverse the order of events by specifying `[["{{_index_}}", "number", true]]`.
#{description_include_sort_info if events == EVENTS_DESCRIPTION}
MD
end

def description_include_sort_info
<<-MD.lstrip
If the `include_sort_info` option is set, each created event will have a `sort_info` key whose value is a hash containing the following keys:
* `position`: 1-based index of each event after the sort
* `count`: Total number of events sorted
MD
end
end
Expand All @@ -54,16 +66,20 @@ def events_order(key = EVENTS_ORDER_KEY)
options[key]
end

def include_sort_info?
boolify(interpolated['include_sort_info'])
end

module AutomaticSorter
def check
return super unless events_order
return super unless events_order || include_sort_info?
sorting_events do
super
end
end

def receive(incoming_events)
return super unless events_order
return super unless events_order || include_sort_info?
# incoming events should be processed sequentially
incoming_events.each do |event|
sorting_events do
Expand All @@ -88,9 +104,20 @@ def sorting_events(&block)
@sortable_events = []
yield
ensure
events, @sortable_events = @sortable_events, nil
sort_events(events).each do |event|
create_event(event)
events, @sortable_events = sort_events(@sortable_events), nil
if include_sort_info?
count = events.count
events.each.with_index(1) do |event, position|
event.payload[:sort_info] = {
position: position,
count: count
}
create_event(event)
end
else
events.each do |event|
create_event(event)
end
end
end
end
Expand Down
60 changes: 54 additions & 6 deletions spec/concerns/sortable_events_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,9 @@ def receive(events)
end
end

def new_agent(events_order = nil)
let :new_agent do
options = {}
options['events_order'] = events_order if events_order
options['events_order'] = @events_order
Agents::EventOrderableAgent.new(name: 'test', options: options) { |agent|
agent.user = users(:bob)
agent.payloads_to_emit = payloads
Expand All @@ -229,25 +229,30 @@ def new_agent(events_order = nil)
}

it 'should keep the order of created events unless events_order is specified' do
[[], [nil], [[]]].each do |args|
agent = new_agent(*args)
[nil, []].each do |events_order|
@events_order = events_order
agent = new_agent
agent.save!
expect { agent.check }.to change { Event.count }.by(4)
events = agent.events.last(4).sort_by(&:id)
expect(events.map(&:payload)).to match_array(payloads)
expect(events.map { |event| event.payload['title'] }).to eq(%w[TitleA TitleB TitleD TitleC])
end
end

it 'should sort events created in check() in the order specified in events_order' do
agent = new_agent([['{{score}}', 'number'], ['{{title}}', 'string', true]])
@events_order = [['{{score}}', 'number'], ['{{title}}', 'string', true]]
agent = new_agent
agent.save!
expect { agent.check }.to change { Event.count }.by(4)
events = agent.events.last(4).sort_by(&:id)
expect(events.map(&:payload)).to match_array(payloads)
expect(events.map { |event| event.payload['title'] }).to eq(%w[TitleB TitleA TitleD TitleC])
end

it 'should sort events created in receive() in the order specified in events_order' do
agent = new_agent([['{{score}}', 'number'], ['{{title}}', 'string', true]])
@events_order = [['{{score}}', 'number'], ['{{title}}', 'string', true]]
agent = new_agent
agent.save!
expect {
agent.receive([Event.new(payload: { 'title_suffix' => ' [new]' }),
Expand All @@ -259,6 +264,49 @@ def new_agent(events_order = nil)
'TitleB [popular]', 'TitleA [popular]', 'TitleD [popular]', 'TitleC [popular]',
])
end

describe 'with the include_sort_info option enabled' do
let :new_agent do
agent = super()
agent.options['include_sort_info'] = true
agent
end

it 'should add sort_info to events created in check() when events_order is not specified' do
agent = new_agent
agent.save!
expect { agent.check }.to change { Event.count }.by(4)
events = agent.events.last(4).sort_by(&:id)
expect(events.map { |event| event.payload['title'] }).to eq(%w[TitleA TitleB TitleD TitleC])
expect(events.map { |event| event.payload['sort_info'] }).to eq((1..4).map{ |pos| { 'position' => pos, 'count' => 4 } })
end

it 'should add sort_info to events created in check() when events_order is specified' do
@events_order = [['{{score}}', 'number'], ['{{title}}', 'string', true]]
agent = new_agent
agent.save!
expect { agent.check }.to change { Event.count }.by(4)
events = agent.events.last(4).sort_by(&:id)
expect(events.map { |event| event.payload['title'] }).to eq(%w[TitleB TitleA TitleD TitleC])
expect(events.map { |event| event.payload['sort_info'] }).to eq((1..4).map{ |pos| { 'position' => pos, 'count' => 4 } })
end

it 'should add sort_info to events created in receive() when events_order is specified' do
@events_order = [['{{score}}', 'number'], ['{{title}}', 'string', true]]
agent = new_agent
agent.save!
expect {
agent.receive([Event.new(payload: { 'title_suffix' => ' [new]' }),
Event.new(payload: { 'title_suffix' => ' [popular]' })])
}.to change { Event.count }.by(8)
events = agent.events.last(8).sort_by(&:id)
expect(events.map { |event| event.payload['title'] }).to eq([
'TitleB [new]', 'TitleA [new]', 'TitleD [new]', 'TitleC [new]',
'TitleB [popular]', 'TitleA [popular]', 'TitleD [popular]', 'TitleC [popular]',
])
expect(events.map { |event| event.payload['sort_info'] }).to eq((1..4).map{ |pos| { 'position' => pos, 'count' => 4 } } * 2)
end
end
end
end
end

0 comments on commit c50e2f5

Please sign in to comment.