Skip to content

Instantly share code, notes, and snippets.

@solnic
Last active August 29, 2015 14:15
Show Gist options
  • Save solnic/e9b2cf1abf4481b0ecbe to your computer and use it in GitHub Desktop.
Save solnic/e9b2cf1abf4481b0ecbe to your computer and use it in GitHub Desktop.
[ROM] composable relations with partial application prototype
require 'rom'
require 'rspec'
module ROM
class Relation
class Composite
attr_reader :left, :right
def initialize(left, right)
@left = left
@right = right
end
def call
right.call(left.call)
end
def to_a
[left.to_a, call.to_a]
end
end
class Lazy
include Options
option :name, type: Symbol, reader: true
option :curry_args, type: Array, reader: true
attr_reader :relation, :method
def initialize(relation, options = {})
super
@relation = relation
@method = relation.method(name) if name
end
def >>(other)
Composite.new(self, other)
end
def to_a
call.to_a
end
def call(*args)
if name
all_args = curry_args + args
if method.arity == curry_args.size
relation.__send__(name, *all_args).to_a
else
self.class.new(relation, name: name, curry_args: all_args)
end
else
relation.to_a
end
end
alias_method :[], :call
private
def method_missing(name, *args)
self.class.new(relation, name: name, curry_args: args)
end
end
end
end
ROM.setup(:memory)
class Users < ROM::Relation[:memory]
def by_name(name)
restrict(name: name)
end
end
class Tasks < ROM::Relation[:memory]
def for_users(users)
ids = users.map { |u| u[:id] }
restrict { |t| ids.include?(t[:user_id]) }
end
end
ROM.finalize
rom = ROM.env
rom.relations.users << { id: 1, name: 'Jane' }
rom.relations.tasks << { user_id: 1, title: 'Task one' }
describe ROM::Relation::Lazy do
let!(:rom) { ROM.env }
let(:users) { ROM::Relation::Lazy.new(rom.relations.users) }
let(:tasks) { ROM::Relation::Lazy.new(rom.relations.tasks) }
describe '#call' do
it 'auto-curries' do
relation = users.by_name
expect(relation.name).to eql(:by_name)
expect(relation['Jane'].to_a).to eql(rom.relations.users.by_name('Jane').to_a)
end
end
describe '#>>' do
it 'composes two relations' do
other = users.by_name('Jane') >> tasks.for_users
expect(other.to_a).to eql([
[{ id: 1, name: 'Jane' }], [{ user_id: 1, title: 'Task one' }]
])
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment