Skip to content

Commit

Permalink
Implement Scopes::Criteria.
Browse files Browse the repository at this point in the history
  • Loading branch information
sleepingkingstudios committed Dec 22, 2023
1 parent 654dc2b commit 75bb072
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 0 deletions.
2 changes: 2 additions & 0 deletions lib/cuprum/collections.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ module Collections
autoload :Relation, 'cuprum/collections/relation'
autoload :Repository, 'cuprum/collections/repository'
autoload :Resource, 'cuprum/collections/resource'
autoload :Scope, 'cuprum/collections/scope'
autoload :Scopes, 'cuprum/collections/scopes'

# @return [String] the absolute path to the gem directory.
def self.gem_path
Expand Down
2 changes: 2 additions & 0 deletions lib/cuprum/collections/rspec/contracts.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,7 @@ module Contracts
'cuprum/collections/rspec/contracts/relation_contracts'
autoload :RepositoryContracts,
'cuprum/collections/rspec/contracts/repository_contracts'
autoload :Scopes,
'cuprum/collections/rspec/contracts/scopes'
end
end
81 changes: 81 additions & 0 deletions lib/cuprum/collections/rspec/contracts/scope_contracts.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# frozen_string_literal: true

require 'cuprum/collections/rspec/contracts'

module Cuprum::Collections::RSpec::Contracts
# Contracts for asserting on scope objects.
module ScopeContracts
# Contract validating the behavior of a Criteria scope implementation.
module ShouldBeACriteriaScopeContract
extend RSpec::SleepingKingStudios::Contract

# @!method apply(example_group)
# Adds the contract to the example group.
#
# @param example_group [RSpec::Core::ExampleGroup] the example group to
# which the contract is applied.
contract do
shared_context 'with criteria' do
let(:criteria) do
[
['title', 'eq', 'Gideon the Ninth'],
['author', 'eq', 'Tamsyn Muir']
]
end
end

let(:criteria) { [] }

describe '.new' do
it 'should define the constructor' do
expect(described_class)
.to be_constructible
.with(0).arguments
.and_keywords(:criteria)
.and_any_keywords
end
end

describe '#criteria' do
include_examples 'should define reader', :criteria, -> { criteria }

wrap_context 'with criteria' do
it { expect(scope.criteria).to be == criteria }
end
end

describe '#with_criteria' do
let(:new_criteria) { ['author', 'eq', 'Ursula K. LeGuin'] }

it { expect(scope).to respond_to(:with_criteria).with(1).argument }

it 'should return a scope' do
expect(scope.with_criteria(new_criteria)).to be_a described_class
end

it "should not change the original scope's criteria" do
expect { scope.with_criteria(new_criteria) }
.not_to change(scope, :criteria)
end

it "should set the copied scope's criteria" do
expect(scope.with_criteria(new_criteria).criteria)
.to be == new_criteria
end

wrap_context 'with criteria' do
it "should not change the original scope's criteria" do
expect { scope.with_criteria(new_criteria) }
.not_to change(scope, :criteria)
end

it "should set the copied scope's criteria" do
expect(scope.with_criteria(new_criteria).criteria)
.to be == new_criteria
end
end
end
end
end
end
end
8 changes: 8 additions & 0 deletions lib/cuprum/collections/scope.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

require 'cuprum/collections'

module Cuprum::Collections
# Abstract class representing a set of filters for a query.
class Scope; end # rubocop:disable Lint/EmptyClass
end
10 changes: 10 additions & 0 deletions lib/cuprum/collections/scopes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

require 'cuprum/collections'

module Cuprum::Collections
# Namespace for scope functionality, which filters query data.
module Scopes
autoload :Criteria, 'cuprum/collections/scopes/criteria'
end
end
31 changes: 31 additions & 0 deletions lib/cuprum/collections/scopes/criteria.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

require 'cuprum/collections/scopes'

module Cuprum::Collections::Scopes
# Functionality for implementing a criteria scope.
module Criteria
# @param criteria [Array] the criteria used for filtering query data.
def initialize(criteria:, **options)
super(**options)

@criteria = criteria
end

# @return [Array] the criteria used for filtering query data.
attr_reader :criteria

# Creates a copy of the scope with the given criteria.
#
# @param criteria [Array] the criteria used for filtering query data.
#
# @return [Scope] the copied scope.
def with_criteria(criteria)
dup.tap { |copy| copy.criteria = criteria }
end

protected

attr_writer :criteria
end
end
19 changes: 19 additions & 0 deletions spec/cuprum/collections/scopes/criteria_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

require 'cuprum/collections/scope'
require 'cuprum/collections/scopes/criteria'
require 'cuprum/collections/rspec/contracts/scope_contracts'

RSpec.describe Cuprum::Collections::Scopes::Criteria do
include Cuprum::Collections::RSpec::Contracts::ScopeContracts

subject(:scope) { described_class.new(criteria: criteria) }

let(:described_class) { Spec::ExampleScope }

example_class 'Spec::ExampleScope', Cuprum::Collections::Scope do |klass|
klass.include Cuprum::Collections::Scopes::Criteria # rubocop:disable RSpec/DescribedClass
end

include_contract 'should be a criteria scope'
end

0 comments on commit 75bb072

Please sign in to comment.