Skip to content

Commit

Permalink
Move Limit#one to Relation#one
Browse files Browse the repository at this point in the history
* Allow #one to be called on unsorted relations

[Fix #60]
  • Loading branch information
dkubb committed Jul 17, 2014
1 parent 2476a75 commit 81bbf10
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 53 deletions.
2 changes: 1 addition & 1 deletion config/reek.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ ControlParameter:
- Axiom::Function::Connective::Conjunction#self.call
- Axiom::Function::Connective::Disjunction#self.call
- Axiom::Function::Predicate::Enumerable#self.compare_method
- Axiom::Relation::Operation::Limit::Methods#assert_no_more_than_one_tuple
- Axiom::Relation#assert_no_more_than_one_tuple
DataClump:
enabled: true
exclude:
Expand Down
48 changes: 48 additions & 0 deletions lib/axiom/relation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ class Relation
include Enumerable, Visitable, Adamantium::Flat
include Equalizer.new(:header, :to_set)

# Default block used in #one
DEFAULT_ONE_BLOCK = -> {}

# Maximum number of tuples to take in #one
ONE_LIMIT = 2

# The relation header
#
# @return [Header]
Expand Down Expand Up @@ -145,6 +151,32 @@ def replace(other)
delete(difference(other)).insert(other.difference(self))
end

# Return a tuple if the relation contains exactly one tuple
#
# @example without a block
# tuple = relation.one
#
# @example with a block
# tuple = relation.one { ... }
#
# @yieldreturn [Object]
#
# @return [Tuple]
#
# @raise [NoTuplesError]
# raised if no tuples are returned
# @raise [ManyTuplesError]
# raised if more than one tuple is returned
#
# @api public
def one(&block)
block ||= DEFAULT_ONE_BLOCK
tuples = sort.take(ONE_LIMIT).to_a
assert_no_more_than_one_tuple(tuples.size)
tuples.first or block.yield or
fail NoTuplesError, 'one tuple expected, but was an empty set'
end

# Return a relation with each tuple materialized
#
# @example
Expand Down Expand Up @@ -245,5 +277,21 @@ def self.coerce(header, object)
end
end

# Assert no more than one tuple is returned
#
# @return [undefined]
#
# @raise [ManyTuplesError]
# raised if more than one tuple is returned
#
# @api private
def assert_no_more_than_one_tuple(size)
return if size <= 1
fail(
ManyTuplesError,
"one tuple expected, but set contained #{count} tuples"
)
end

end # class Relation
end # module Axiom
50 changes: 0 additions & 50 deletions lib/axiom/relation/operation/limit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,6 @@ def delete(*)

module Methods

# Default block used in #one
DEFAULT_ONE_BLOCK = -> {}

# Maximum number of tuples to take in #one
ONE_LIMIT = 2

# Return a relation with n tuples
#
# @example
Expand Down Expand Up @@ -207,50 +201,6 @@ def last(limit = 1)
reverse.take(limit).reverse
end

# Return a tuple if the relation contains exactly one tuple
#
# @example without a block
# tuple = relation.one
#
# @example with a block
# tuple = relation.one { ... }
#
# @yieldreturn [Object]
#
# @return [Tuple]
#
# @raise [NoTuplesError]
# raised if no tuples are returned
# @raise [ManyTuplesError]
# raised if more than one tuple is returned
#
# @api public
def one(&block)
block ||= DEFAULT_ONE_BLOCK
tuples = take(ONE_LIMIT).to_a
assert_no_more_than_one_tuple(tuples.size)
tuples.first or block.yield or
fail NoTuplesError, 'one tuple expected, but was an empty set'
end

private

# Assert no more than one tuple is returned
#
# @return [undefined]
#
# @raise [ManyTuplesError]
# raised if more than one tuple is returned
#
# @api private
def assert_no_more_than_one_tuple(size)
return if size <= 1
fail(
ManyTuplesError,
"one tuple expected, but set contained #{count} tuples"
)
end

end # module Methods

Relation.class_eval { include Methods }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

require 'spec_helper'

describe Relation::Operation::Limit::Methods, '#one' do
describe Relation, '#one' do
subject { object.one }

let(:object) { Relation.new(header, body).sort }
let(:object) { Relation.new(header, body) }
let(:header) { Relation::Header.coerce([[:id, Integer]]) }

context 'with a relation having no tuples without a block' do
Expand Down

0 comments on commit 81bbf10

Please sign in to comment.