From 23edcee47d96ef997d5f682cc7d7061d2cbe29b7 Mon Sep 17 00:00:00 2001 From: Alex Flores Date: Thu, 9 Jun 2016 13:21:54 -0400 Subject: [PATCH 1/2] add find_in_batches-like functionality - add specs - implement for activerecord adapter - implement ruby fallback version --- lib/policy_machine.rb | 11 +++ .../active_record.rb | 4 + .../support/shared_examples_policy_machine.rb | 81 +++++++++++++++++++ 3 files changed, 96 insertions(+) diff --git a/lib/policy_machine.rb b/lib/policy_machine.rb index 5e68d3ac..a166bec8 100644 --- a/lib/policy_machine.rb +++ b/lib/policy_machine.rb @@ -194,6 +194,17 @@ def scoped_privileges(user_or_attribute, object_or_attribute, options = {}) end end + ## + # Search for and iterate over a collection in batches + def batch_find(type:, query: {}, config: {}, &blk) + if policy_machine_storage_adapter.respond_to?(:batch_find) + policy_machine_storage_adapter.batch_find(type, query, config, &blk) + else + batch_size = config.fetch(:batch_size, 1) + method(type.to_s.pluralize).call(query).each_slice(batch_size, &blk) + end + end + ## # Returns an array of all objects the given user (attribute) # has the given operation on. diff --git a/lib/policy_machine_storage_adapters/active_record.rb b/lib/policy_machine_storage_adapters/active_record.rb index b9f61ef8..4553fa96 100644 --- a/lib/policy_machine_storage_adapters/active_record.rb +++ b/lib/policy_machine_storage_adapters/active_record.rb @@ -345,6 +345,10 @@ def scoped_privileges(user_or_attribute, object_or_attribute, options = {}) end end + def batch_find(policy_object, query = {}, config = {}, &blk) + method("find_all_of_type_#{policy_object}").call(query).find_in_batches(config, &blk) + end + ## Optimized version of PolicyMachine#scoped_privileges # Returns all objects the user has the given operation on # TODO: Support multiple policy classes here diff --git a/spec/support/shared_examples_policy_machine.rb b/spec/support/shared_examples_policy_machine.rb index 447382cb..5762e8f2 100644 --- a/spec/support/shared_examples_policy_machine.rb +++ b/spec/support/shared_examples_policy_machine.rb @@ -820,4 +820,85 @@ end + + describe 'batch_find' do + + before do + @one_fish = policy_machine.create_object('one:fish') + @two_fish = policy_machine.create_object('two:fish') + @red_one = policy_machine.create_object('red:one') + @read = policy_machine.create_operation('read') + @write = policy_machine.create_operation('write') + @u1 = policy_machine.create_user('u1') + @ua = policy_machine.create_user_attribute('ua') + [@one_fish, @two_fish, @red_one].each do |object| + policy_machine.add_association(@ua, Set.new([@read]), object) + end + @oa = policy_machine.create_object_attribute('oa') + policy_machine.add_association(@ua, Set.new([@write]), @oa) + policy_machine.add_assignment(@u1, @ua) + policy_machine.add_assignment(@red_one, @oa) + end + + context 'when given a block' do + + context 'and search terms' do + it 'returns the matching records' do + policy_machine.batch_find(type: :object, query: { unique_identifier: 'one:fish' }) do |batch| + expect(batch.size).to eq 1 + expect(batch.first.unique_identifier).to eq 'one:fish' + end + end + end + + context 'and config options' do + it 'returns the correct batch size' do + policy_machine.batch_find(type: :object, config: { batch_size: 1 }) do |batch| + expect(batch.size).to eq 1 + end + end + end + end + + context 'when not given a block' do + + it 'returns an enumerator' do + result = policy_machine.batch_find(type: :object) + expect(result).to be_a Enumerator + end + + it 'the results are chainable and returns the relevant results' do + enum = policy_machine.batch_find(type: :object) + results = enum.flat_map do |batch| + batch.map { |pe| pe.unique_identifier } + end + expected = %w(one:fish two:fish red:one) + expect(results).to include(*expected) + end + + context 'but given search terms' do + it 'the results are chainable and returns the relevant results' do + enum = policy_machine.batch_find(type: :object, query: { unique_identifier: 'one:fish' }) + results = enum.flat_map do |batch| + batch.map { |pe| pe.unique_identifier } + end + expected = 'one:fish' + expect(results.first).to eq(expected) + end + end + + context 'but given config options' do + it 'resepects batch size configs while return all results' do + enum = policy_machine.batch_find(type: :object, config: { batch_size: 1}) + results = enum.flat_map do |batch| + expect(batch.size).to eq 1 + batch.map { |pe| pe.unique_identifier } + end + expected = %w(one:fish two:fish red:one) + expect(results).to include(*expected) + end + end + + end + end end From 12eda22eada37d7a7dc5859edfdd3a70bf7a6660 Mon Sep 17 00:00:00 2001 From: Alex Flores Date: Mon, 13 Jun 2016 11:53:05 -0400 Subject: [PATCH 2/2] improve example - test that methods yield to blocks - ensure batch size options work --- spec/support/shared_examples_policy_machine.rb | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/spec/support/shared_examples_policy_machine.rb b/spec/support/shared_examples_policy_machine.rb index 5762e8f2..e6ffafc5 100644 --- a/spec/support/shared_examples_policy_machine.rb +++ b/spec/support/shared_examples_policy_machine.rb @@ -842,6 +842,12 @@ context 'when given a block' do + it 'calls the block' do + expect do |spy| + policy_machine.batch_find(type: :object, query: { unique_identifier: 'one:fish' }, &spy) + end.to yield_control + end + context 'and search terms' do it 'returns the matching records' do policy_machine.batch_find(type: :object, query: { unique_identifier: 'one:fish' }) do |batch| @@ -856,6 +862,10 @@ policy_machine.batch_find(type: :object, config: { batch_size: 1 }) do |batch| expect(batch.size).to eq 1 end + + policy_machine.batch_find(type: :object, config: { batch_size: 3 }) do |batch| + expect(batch.size).to eq 3 + end end end end @@ -889,9 +899,9 @@ context 'but given config options' do it 'resepects batch size configs while return all results' do - enum = policy_machine.batch_find(type: :object, config: { batch_size: 1}) + enum = policy_machine.batch_find(type: :object, config: { batch_size: 3}) results = enum.flat_map do |batch| - expect(batch.size).to eq 1 + expect(batch.size).to eq 3 batch.map { |pe| pe.unique_identifier } end expected = %w(one:fish two:fish red:one)