Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Property predicates #48

Merged
merged 19 commits into from
Dec 2, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Rex.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
D86FFBDA1B34B3F0001A89B3 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD91B34B3F0001A89B3 /* Action.swift */; };
D86FFBDB1B34B3F0001A89B3 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD91B34B3F0001A89B3 /* Action.swift */; };
D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBDC1B34B691001A89B3 /* UIButton.swift */; };
D8A454061BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; };
D8A454071BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; };
D8A454091BD2772700C9E790 /* PropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PropertyTests.swift */; };
D8A4540A1BD2772700C9E790 /* PropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PropertyTests.swift */; };
D8E4A6201B7BBB1600EAD8A8 /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */; };
D8E4A6211B7BBB2100EAD8A8 /* UIBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5173EBC51B625A2600C9B48E /* UIBarItem.swift */; };
D8F073161B863CE70047D546 /* UILabelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F073141B861B3A0047D546 /* UILabelTests.swift */; };
Expand Down Expand Up @@ -129,6 +133,8 @@
D86FFBD71B34B242001A89B3 /* UILabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabel.swift; sourceTree = "<group>"; };
D86FFBD91B34B3F0001A89B3 /* Action.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Action.swift; sourceTree = "<group>"; };
D86FFBDC1B34B691001A89B3 /* UIButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButton.swift; sourceTree = "<group>"; };
D8A454051BD26A1A00C9E790 /* Property.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Property.swift; sourceTree = "<group>"; };
D8A454081BD2772700C9E790 /* PropertyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PropertyTests.swift; sourceTree = "<group>"; };
D8F073141B861B3A0047D546 /* UILabelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabelTests.swift; sourceTree = "<group>"; };
D8F0973A1B17F2F7002E15BA /* NSData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSData.swift; sourceTree = "<group>"; };
D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSUserDefaults.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -197,6 +203,7 @@
isa = PBXGroup;
children = (
D86FFBD91B34B3F0001A89B3 /* Action.swift */,
D8A454051BD26A1A00C9E790 /* Property.swift */,
D8003EBC1AFED01000D7D3C5 /* Signal.swift */,
D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */,
4238D5941B4D593E008534C0 /* AppKit */,
Expand All @@ -219,6 +226,7 @@
D8003E9D1AFEC3D400D7D3C5 /* Tests */ = {
isa = PBXGroup;
children = (
D8A454081BD2772700C9E790 /* PropertyTests.swift */,
D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */,
D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */,
D8F097461B17F5BF002E15BA /* Foundation */,
Expand Down Expand Up @@ -489,6 +497,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D8A454061BD26A1A00C9E790 /* Property.swift in Sources */,
D86FFBDA1B34B3F0001A89B3 /* Action.swift in Sources */,
D86FFBD11B34AD6F001A89B3 /* Association.swift in Sources */,
D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */,
Expand All @@ -507,6 +516,7 @@
D8F0974A1B17F5E1002E15BA /* NSObjectTests.swift in Sources */,
D8003EC21AFED30F00D7D3C5 /* SignalTests.swift in Sources */,
D8003EC31AFED30F00D7D3C5 /* SignalProducerTests.swift in Sources */,
D8A454091BD2772700C9E790 /* PropertyTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -521,6 +531,7 @@
D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */,
D834572D1AFEE45B0070616A /* Signal.swift in Sources */,
D8E4A6211B7BBB2100EAD8A8 /* UIBarItem.swift in Sources */,
D8A454071BD26A1A00C9E790 /* Property.swift in Sources */,
D8E4A6201B7BBB1600EAD8A8 /* UIBarButtonItem.swift in Sources */,
D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */,
D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */,
Expand All @@ -540,6 +551,7 @@
8289A2E81BD7F7900097FB60 /* UIViewTests.swift in Sources */,
D8F073161B863CE70047D546 /* UILabelTests.swift in Sources */,
8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */,
D8A4540A1BD2772700C9E790 /* PropertyTests.swift in Sources */,
D83457301AFEE45E0070616A /* SignalProducerTests.swift in Sources */,
D83457411AFEE6050070616A /* SignalTests.swift in Sources */,
8295FD8D1B87374A007C9000 /* UIBarButtonItemTests.swift in Sources */,
Expand Down
107 changes: 107 additions & 0 deletions Source/Property.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
//
// Property.swift
// Rex
//
// Created by Neil Pankey on 10/17/15.
// Copyright (c) 2015 Neil Pankey. All rights reserved.
//

import ReactiveCocoa

extension PropertyType where Value == Bool {
public func and<P: PropertyType where P.Value == Bool>(other: P) -> AndProperty {
return AndProperty(terms: [AnyProperty(self), AnyProperty(other)])
}

public func and(other: AnyProperty<Bool>) -> AndProperty {
return AndProperty(terms: [AnyProperty(self), other])
}

public func or<P: PropertyType where P.Value == Bool>(other: P) -> OrProperty {
return OrProperty(terms: [AnyProperty(self), AnyProperty(other)])
}

public func or(other: AnyProperty<Bool>) -> OrProperty {
return OrProperty(terms: [AnyProperty(self), other])
}

public func not() -> NotProperty {
return NotProperty(source: AnyProperty(self), invert: true)
}
}

public struct AndProperty: PropertyType {
public let terms: [AnyProperty<Bool>]

public var value: Bool {
return terms.reduce(true) { $0 && $1.value }
}

public var producer: SignalProducer<Bool, NoError> {
let producers = terms.map { $0.producer }
return combineLatest(producers).map { values in
return values.reduce(true) { $0 && $1 }
}
}

public func and<P : PropertyType where P.Value == Bool>(other: P) -> AndProperty {
return AndProperty(terms: terms + [AnyProperty(other)])
}

public func and(other: AnyProperty<Bool>) -> AndProperty {
return AndProperty(terms: terms + [other])
}

private init(terms: [AnyProperty<Bool>]) {
self.terms = terms
}
}

public struct OrProperty: PropertyType {
public let terms: [AnyProperty<Bool>]

public var value: Bool {
return terms.reduce(false) { $0 || $1.value }
}

public var producer: SignalProducer<Bool, NoError> {
let producers = terms.map { $0.producer }
return combineLatest(producers).map { values in
return values.reduce(false) { $0 || $1 }
}
}

public func or<P : PropertyType where P.Value == Bool>(other: P) -> OrProperty {
return OrProperty(terms: terms + [AnyProperty(other)])
}

public func or(other: AnyProperty<Bool>) -> OrProperty {
return OrProperty(terms: terms + [other])
}

private init(terms: [AnyProperty<Bool>]) {
self.terms = terms
}
}

public struct NotProperty: PropertyType {
private let source: AnyProperty<Bool>
private let invert: Bool

public var value: Bool {
return source.value != invert
}

public var producer: SignalProducer<Bool, NoError> {
return source.producer.map { $0 != self.invert }
}

public func not() -> NotProperty {
return NotProperty(source: source, invert: !invert)
}

private init(source: AnyProperty<Bool>, invert: Bool) {
self.source = source
self.invert = invert
}
}
89 changes: 89 additions & 0 deletions Tests/PropertyTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//
// PropertyTests.swift
// Rex
//
// Created by Neil Pankey on 10/17/15.
// Copyright (c) 2015 Neil Pankey. All rights reserved.
//

@testable import Rex
import ReactiveCocoa
import XCTest

final class PropertyTests: XCTestCase {

func testAndProperty() {
let lhs = MutableProperty(false), rhs = MutableProperty(false)
let and = lhs.and(rhs)

var current: Bool!
and.producer.startWithNext { current = $0 }

XCTAssertFalse(and.value)
XCTAssertFalse(current!)

lhs.value = true
XCTAssertFalse(and.value)
XCTAssertFalse(current!)

rhs.value = true
XCTAssertTrue(and.value)
XCTAssertTrue(current!)

let (signal, pipe) = Signal<Bool, NoError>.pipe()
let and2 = and.and(AnyProperty(initialValue: false, signal: signal))
and2.producer.startWithNext { current = $0 }

XCTAssertFalse(and2.value)
XCTAssertFalse(current!)

pipe.sendNext(true)
XCTAssertTrue(and2.value)
XCTAssertTrue(current!)
}

func testOrProperty() {
let lhs = MutableProperty(true), rhs = MutableProperty(true)
let or = lhs.or(rhs)

var current: Bool!
or.producer.startWithNext { current = $0 }

XCTAssertTrue(or.value)
XCTAssertTrue(current!)

lhs.value = false
XCTAssertTrue(or.value)
XCTAssertTrue(current!)

rhs.value = false
XCTAssertFalse(or.value)
XCTAssertFalse(current!)

let (signal, pipe) = Signal<Bool, NoError>.pipe()
let or2 = or.or(AnyProperty(initialValue: true, signal: signal))
or2.producer.startWithNext { current = $0 }

XCTAssertTrue(or2.value)
XCTAssertTrue(current!)

pipe.sendNext(false)
XCTAssertFalse(or2.value)
XCTAssertFalse(current!)
}

func testNotProperty() {
let source = MutableProperty(false)
let not = source.not()

var current: Bool!
not.producer.startWithNext { current = $0 }

XCTAssertTrue(not.value)
XCTAssertTrue(current!)

source.value = true
XCTAssertFalse(not.value)
XCTAssertFalse(current!)
}
}