Skip to content

Commit

Permalink
Added property intellisense to the ruleset creator, although its comp…
Browse files Browse the repository at this point in the history
…letely optional to use, it also should have been called `intellisense-properties` as there is no type safety.
  • Loading branch information
grofit committed Aug 15, 2016
1 parent 221cfc7 commit 143a5cd
Show file tree
Hide file tree
Showing 14 changed files with 130 additions and 54 deletions.
17 changes: 15 additions & 2 deletions dist/commonjs/builders/ruleset-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
var ruleset_1 = require("../rulesets/ruleset");
var rule_link_1 = require("../rulesets/rule-link");
var for_each_rule_1 = require("../rulesets/for-each-rule");
var type_helper_1 = require("../helpers/type-helper");
var RulesetBuilder = (function () {
function RulesetBuilder(ruleRegistry) {
var _this = this;
Expand All @@ -11,8 +12,15 @@ var RulesetBuilder = (function () {
_this.currentProperty = null;
return _this;
};
this.forProperty = function (propertyName) {
_this.currentProperty = propertyName;
this.forProperty = function (propertyNameOrPredicate) {
var endProperty = propertyNameOrPredicate;
if (type_helper_1.TypeHelper.isFunctionType(endProperty)) {
endProperty = _this.extractPropertyName(propertyNameOrPredicate);
if (!endProperty) {
throw new Error("cannot resolve property from: " + propertyNameOrPredicate);
}
}
_this.currentProperty = endProperty;
_this.currentRule = null;
return _this;
};
Expand Down Expand Up @@ -76,6 +84,11 @@ var RulesetBuilder = (function () {
return _this.internalRuleset;
};
}
RulesetBuilder.prototype.extractPropertyName = function (predicate) {
var regex = /.*\.([\w]*);/;
var predicateString = predicate.toString();
return regex.exec(predicateString)[1];
};
return RulesetBuilder;
}());
exports.RulesetBuilder = RulesetBuilder;
2 changes: 1 addition & 1 deletion dist/commonjs/exposer.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"use strict";
var field_error_processor_1 = require("./processors/field-error-processor");
var ruleset_builder_1 = require("./builders/ruleset-builder");
var rule_resolver_1 = require("./rulesets/rule-resolver");
var validation_group_builder_1 = require("./builders/validation-group-builder");
var rule_registry_setup_1 = require("./rule-registry-setup");
var ruleset_builder_1 = require("./builders/ruleset-builder");
var fieldErrorProcessor = new field_error_processor_1.FieldErrorProcessor(rule_registry_setup_1.ruleRegistry);
var ruleResolver = new rule_resolver_1.RuleResolver();
function createRuleset(withRuleVerification) {
Expand Down
6 changes: 3 additions & 3 deletions dist/commonjs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ __export(require("./processors/field-error-processor"));
__export(require("./processors/field-has-error"));
__export(require("./processors/validation-error"));
__export(require("./promises/promise-counter"));
__export(require("./resolvers/model-resolver"));
__export(require("./rules/advanced-regex-rule"));
__export(require("./rules/date-validation-rule"));
__export(require("./rules/decimal-validation-rule"));
Expand All @@ -35,12 +36,11 @@ __export(require("./rules/regex-validation-rule"));
__export(require("./rules/required-validation-rule"));
__export(require("./rules/rule-registry"));
__export(require("./rules/step-validation-rule"));
__export(require("./resolvers/model-resolver"));
__export(require("./watcher/model-watcher"));
__export(require("./watcher/property-watcher"));
__export(require("./rulesets/for-each-rule"));
__export(require("./rulesets/rule-link"));
__export(require("./rulesets/rule-resolver"));
__export(require("./rulesets/ruleset"));
__export(require("./validation-groups/reactive-validation-group"));
__export(require("./validation-groups/validation-group"));
__export(require("./watcher/model-watcher"));
__export(require("./watcher/property-watcher"));
25 changes: 13 additions & 12 deletions dist/definitions/builders/ruleset-builder.d.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import { Ruleset } from "../rulesets/ruleset";
import { RuleLink } from "../rulesets/rule-link";
import { RuleRegistry } from "../rules/rule-registry";
export declare class RulesetBuilder {
export declare class RulesetBuilder<T> {
private ruleRegistry;
private internalRuleset;
currentProperty: string;
currentRule: RuleLink;
protected internalRuleset: Ruleset;
protected currentProperty: string;
protected currentRule: RuleLink;
constructor(ruleRegistry?: RuleRegistry);
create: () => RulesetBuilder;
forProperty: (propertyName: string) => RulesetBuilder;
addRule: (rule: string, ruleOptions?: any) => RulesetBuilder;
withMessage: (messageOverride: ((value: any, ruleOptions?: any) => string) | string) => RulesetBuilder;
appliesIf: (appliesFunction: ((model: any, value: any, ruleOptions?: any) => boolean) | boolean) => RulesetBuilder;
addRuleForEach: (rule: string, ruleOptions?: any) => RulesetBuilder;
addRuleset: (ruleset: Ruleset) => RulesetBuilder;
addRulesetForEach: (ruleset: Ruleset) => RulesetBuilder;
protected extractPropertyName(predicate: (model: T) => any): string;
create: () => RulesetBuilder<T>;
forProperty: (propertyNameOrPredicate: ((model: T) => any) | string) => RulesetBuilder<T>;
addRule: (rule: string, ruleOptions?: any) => RulesetBuilder<T>;
withMessage: (messageOverride: ((value: any, ruleOptions?: any) => string) | string) => RulesetBuilder<T>;
appliesIf: (appliesFunction: ((model: any, value: any, ruleOptions?: any) => boolean) | boolean) => RulesetBuilder<T>;
addRuleForEach: (rule: string, ruleOptions?: any) => RulesetBuilder<T>;
addRuleset: (ruleset: Ruleset) => RulesetBuilder<T>;
addRulesetForEach: (ruleset: Ruleset) => RulesetBuilder<T>;
build: () => Ruleset;
}
4 changes: 2 additions & 2 deletions dist/definitions/exposer.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { RulesetBuilder } from "./builders/ruleset-builder";
import { ValidationGroupBuilder } from "./builders/validation-group-builder";
export declare function createRuleset(withRuleVerification?: boolean): RulesetBuilder;
import { RulesetBuilder } from "./builders/ruleset-builder";
export declare function createRuleset<T>(withRuleVerification?: boolean): RulesetBuilder<T>;
export declare function createGroup(): ValidationGroupBuilder;
12 changes: 6 additions & 6 deletions dist/definitions/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ export * from "./processors/field-has-error";
export * from "./processors/ifield-error-processor";
export * from "./processors/validation-error";
export * from "./promises/promise-counter";
export * from "./resolvers/imodel-resolver";
export * from "./resolvers/iproperty-resolver";
export * from "./resolvers/model-resolver";
export * from "./rules/advanced-regex-rule";
export * from "./rules/date-validation-rule";
export * from "./rules/decimal-validation-rule";
Expand All @@ -35,12 +38,6 @@ export * from "./rules/regex-validation-rule";
export * from "./rules/required-validation-rule";
export * from "./rules/rule-registry";
export * from "./rules/step-validation-rule";
export * from "./resolvers/imodel-resolver";
export * from "./resolvers/iproperty-resolver";
export * from "./resolvers/model-resolver";
export * from "./watcher/imodel-watcher";
export * from "./watcher/model-watcher";
export * from "./watcher/property-watcher";
export * from "./rulesets/for-each-rule";
export * from "./rulesets/irule-resolver";
export * from "./rulesets/rule-link";
Expand All @@ -50,3 +47,6 @@ export * from "./validation-groups/ireactive-validation-group";
export * from "./validation-groups/ivalidation-group";
export * from "./validation-groups/reactive-validation-group";
export * from "./validation-groups/validation-group";
export * from "./watcher/imodel-watcher";
export * from "./watcher/model-watcher";
export * from "./watcher/property-watcher";
14 changes: 12 additions & 2 deletions docs/creating-rulesets.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ are very lightweight objects with no logic.

When creating a ruleset there are the following methods:

### forProperty(propertyName: string)
### forProperty = (propertyNameOrPredicate: ((model: T) => any) | string): RulesetBuilder<T>

This takes a string which should be the property name that you wish to apply rules to.
This takes a string which should be the property name that you wish to apply rules to or a predicate which
links to the property, although this is mainly just for typescript users.

### addRule(ruleType: string, ruleArgs?: any)

Expand Down Expand Up @@ -66,6 +67,15 @@ So the above example is basically setting up a ruleset where it expects a proper
should have a `maxLength` of 5. You can easily add multiple rules or nested rules to properties allowing for a
very flexible and composite approach to ruleset building and management.

## Simplest Example With Typescript Intellisense

```ts
var ruleset = Treacherous.createRuleset<SomeModel>()
.addProperty(x => x.SomeProperty)
.addRule("required")
.build();
```

## More Complex Example

```js
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@
"gulp-uglify": "latest",
"karma": "1.1.1",
"karma-firefox-launcher": "latest",
"karma-mocha": "latest",
"karma-chrome-launcher": "latest",
"karma-phantomjs-launcher": "^1.0.1",
"karma-mocha": "latest",
"karma-webpack": "1.7.0",
"merge2": "latest",
"mkdirp": "latest",
Expand Down
12 changes: 12 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,18 @@ var validationGroup = Treacherous.createGroup()
validationGroup.propertyStateChangedEvent.subscribe(function(propertyValidationChangedEvent){...));
```
## Typescript users
As typescript users you can get some nicer features and intellisense so you can create typed rules allowing
you to use lambda style property location like so:
```ts
var ruleset = Treacherous.createRuleset<SomeModel>()
.addProperty(x => x.SomeProperty)
.addRule("required")
.build();
```
---
## Validation rules
Expand Down
40 changes: 27 additions & 13 deletions src/builders/ruleset-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,44 @@ import {Ruleset} from "../rulesets/ruleset";
import {RuleLink} from "../rulesets/rule-link";
import {ForEachRule} from "../rulesets/for-each-rule";
import {RuleRegistry} from "../rules/rule-registry";
import {TypeHelper} from "../helpers/type-helper";

export class RulesetBuilder
export class RulesetBuilder<T>
{
private internalRuleset: Ruleset;
public currentProperty: string;
public currentRule:RuleLink;
protected internalRuleset: Ruleset;
protected currentProperty: string;
protected currentRule:RuleLink;

constructor(private ruleRegistry?: RuleRegistry) {}

public create = (): RulesetBuilder =>
protected extractPropertyName(predicate: (model: T) => any) : string {
var regex = /.*\.([\w]*);/;
var predicateString = predicate.toString();
return regex.exec(predicateString)[1];
}

public create = (): RulesetBuilder<T> =>
{
this.internalRuleset = new Ruleset();
this.currentProperty = null;
return this;
}

public forProperty = (propertyName: string): RulesetBuilder =>
public forProperty = (propertyNameOrPredicate: ((model: T) => any) | string): RulesetBuilder<T> =>
{
this.currentProperty = propertyName;
var endProperty = propertyNameOrPredicate;
if(TypeHelper.isFunctionType(endProperty))
{
endProperty = this.extractPropertyName(<any>propertyNameOrPredicate);
if(!endProperty) { throw new Error(`cannot resolve property from: ${propertyNameOrPredicate}`); }
}

this.currentProperty = <string>endProperty;
this.currentRule = null;
return this;
}

public addRule = (rule: string, ruleOptions?: any): RulesetBuilder =>
public addRule = (rule: string, ruleOptions?: any): RulesetBuilder<T> =>
{
if(rule == null || typeof(rule) == "undefined" || rule.length == 0)
{ throw new Error("A rule name is required"); }
Expand All @@ -40,7 +54,7 @@ export class RulesetBuilder
return this;
}

public withMessage = (messageOverride: ((value: any, ruleOptions?: any) => string) | string): RulesetBuilder =>
public withMessage = (messageOverride: ((value: any, ruleOptions?: any) => string) | string): RulesetBuilder<T> =>
{
if(!this.currentRule)
{ throw new Error("A message override must precede an addRule call in the chain"); }
Expand All @@ -49,15 +63,15 @@ export class RulesetBuilder
return this;
}

public appliesIf = (appliesFunction: ((model: any, value: any, ruleOptions?: any) => boolean) | boolean): RulesetBuilder =>
public appliesIf = (appliesFunction: ((model: any, value: any, ruleOptions?: any) => boolean) | boolean): RulesetBuilder<T> =>
{
if(!this.currentRule)
{ throw new Error("An appliesIf function must precede an addRule call in the chain"); }
this.currentRule.appliesIf = appliesFunction;
return this;
}

public addRuleForEach = (rule: string, ruleOptions?: any): RulesetBuilder =>
public addRuleForEach = (rule: string, ruleOptions?: any): RulesetBuilder<T> =>
{
if(rule == null || typeof(rule) == "undefined" || rule.length == 0)
{ throw new Error("A rule name is required"); }
Expand All @@ -75,7 +89,7 @@ export class RulesetBuilder
return this;
}

public addRuleset = (ruleset: Ruleset): RulesetBuilder =>
public addRuleset = (ruleset: Ruleset): RulesetBuilder<T> =>
{
if(!this.currentProperty)
{ throw new Error("A property must precede any rule calls in the chain"); }
Expand All @@ -84,7 +98,7 @@ export class RulesetBuilder
return this;
}

public addRulesetForEach = (ruleset: Ruleset): RulesetBuilder =>
public addRulesetForEach = (ruleset: Ruleset): RulesetBuilder<T> =>
{
if(!this.currentProperty)
{ throw new Error("A property must precede any rule calls in the chain"); }
Expand Down
6 changes: 3 additions & 3 deletions src/exposer.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import {FieldErrorProcessor} from "./processors/field-error-processor";
import {RulesetBuilder} from "./builders/ruleset-builder";
import {RuleResolver} from "./rulesets/rule-resolver";
import {ValidationGroupBuilder} from "./builders/validation-group-builder";
import {ruleRegistry} from "./rule-registry-setup";
import {RulesetBuilder} from "./builders/ruleset-builder";

var fieldErrorProcessor = new FieldErrorProcessor(ruleRegistry);
var ruleResolver = new RuleResolver();

export function createRuleset(withRuleVerification = false): RulesetBuilder
export function createRuleset<T>(withRuleVerification = false): RulesetBuilder<T>
{
var rulesetBuilder = withRuleVerification ? new RulesetBuilder(ruleRegistry) : new RulesetBuilder();
var rulesetBuilder = withRuleVerification ? new RulesetBuilder<T>(ruleRegistry) : new RulesetBuilder();
return rulesetBuilder.create();
}

Expand Down
12 changes: 6 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ export * from "./processors/field-has-error"
export * from "./processors/ifield-error-processor"
export * from "./processors/validation-error"
export * from "./promises/promise-counter"
export * from "./resolvers/imodel-resolver"
export * from "./resolvers/iproperty-resolver"
export * from "./resolvers/model-resolver"
export * from "./rules/advanced-regex-rule"
export * from "./rules/date-validation-rule"
export * from "./rules/decimal-validation-rule"
Expand All @@ -35,12 +38,6 @@ export * from "./rules/regex-validation-rule"
export * from "./rules/required-validation-rule"
export * from "./rules/rule-registry"
export * from "./rules/step-validation-rule"
export * from "./resolvers/imodel-resolver"
export * from "./resolvers/iproperty-resolver"
export * from "./resolvers/model-resolver"
export * from "./watcher/imodel-watcher"
export * from "./watcher/model-watcher"
export * from "./watcher/property-watcher"
export * from "./rulesets/for-each-rule"
export * from "./rulesets/irule-resolver"
export * from "./rulesets/rule-link"
Expand All @@ -50,3 +47,6 @@ export * from "./validation-groups/ireactive-validation-group"
export * from "./validation-groups/ivalidation-group"
export * from "./validation-groups/reactive-validation-group"
export * from "./validation-groups/validation-group"
export * from "./watcher/imodel-watcher"
export * from "./watcher/model-watcher"
export * from "./watcher/property-watcher"
26 changes: 26 additions & 0 deletions tests/specs/ruleset-builder-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,32 @@ import {Ruleset} from "../../src/rulesets/ruleset";

describe('Ruleset Builder', function () {

it('should correctly resolve property predicates to property names', function () {
class DummyModel
{
public property1: string;
public property2: number;
}

var dummyRuleRegistry = { hasRuleNamed: function(){ return true; }};
var rulesetBuilder = new RulesetBuilder<DummyModel>(<any>dummyRuleRegistry);

var ruleset: any = rulesetBuilder.create()
.forProperty(x => x.property1)
.addRule("required", true)
.forProperty(x => x.property2)
.addRule("required", true)
.build();

expect(ruleset).not.to.be.null;
expect(ruleset.rules).to.include.keys("property1");
expect(ruleset.rules.property1.length).to.equal(1);
expect(ruleset.rules.property1[0]).to.eql(new RuleLink("required", true));
expect(ruleset.rules).to.include.keys("property2");
expect(ruleset.rules.property2.length).to.equal(1);
expect(ruleset.rules.property2[0]).to.eql(new RuleLink("required", true));
});

it('should correctly add rules to the ruleset', function () {
var dummyRuleRegistry = { hasRuleNamed: function(){ return true; }};
var rulesetBuilder = new RulesetBuilder(<any>dummyRuleRegistry);
Expand Down
5 changes: 2 additions & 3 deletions tests/specs/treacherous-sanity-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,7 @@ describe('Treacherous Sanity Checks', function () {
}).catch(done);
});

// TODO: This needs fixing but needs discussion on purely state checks
it.skip("should correctly be invalid after changes when reactive", function(done){
it("should correctly be invalid after changes when reactive and validate on startup", function(done){
var ruleSet = createRuleset()
.forProperty("stringValue1").addRule("required")
.forProperty("stringValue2").addRule("required")
Expand All @@ -117,7 +116,7 @@ describe('Treacherous Sanity Checks', function () {
};

var isValid;
var valGroup = createGroup().asReactiveGroup().build(model, ruleSet);
var valGroup = createGroup().asReactiveGroup().andValidateOnStart().build(model, ruleSet);
valGroup.modelStateChangedEvent.subscribe(event => {
console.log("changing state:", event);
isValid = event.isValid
Expand Down

0 comments on commit 143a5cd

Please sign in to comment.