-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy paththing-builder.js
166 lines (148 loc) · 4.89 KB
/
thing-builder.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/**
* Thing class.
* Thing is constructed using new.
*
* let fluffykins = new Thing('Fluffykins')
*
* __define properties__
* fluffykins.is_a.cat
* fluffykins.is_a_cat // true
* fluffykins.is_not_a.dog
* fluffykins.is_a_dog // false
* fluffykins.is_the.terror_of.giant_rodents
* fluffykins.terror_of // giant_rodents
* fluffykins.has(4).legs
* fluffykins.legs.length // 4
* fluffykins.has(2).fangs.each(fang => being_the.slayer_of.heroes.and_the.tormentor_of.women)
* fluffykins.has(2).eyes.each(eye => having(1).laser_cannon)
* fluffkyins.eyes[0].name // eye
*
* __define methods__
* fluffykins.can(jumpOnto(place => `${name} jumps onto ${place}`)
* fluffykins.jumpOnto('table') // 'Fluffykins jumps onto table.'
*
* __remembers previous method calls in 'past-tense' attribute if defined__
* fluffykins.can(slitherInto('slitheredInto', place => `${name} slithers into ${place}`)
* fluffykins.slitherInto('corner') // 'Fluffykins slithers into corner'
* fluffykins.slitheredInto // ['Fluffykins slithers into corner']
*/
class Thing {
constructor (name) {
this.name = name
Array.prototype.each = function(cb) {
var cbStr = cb.toString()
var arg = cbStr.match(/^\(?\w+/)[0]
var fnBody = cbStr.replace(/\w+ => /, "")
var newFnBody = arg + "." + fnBody
this.forEach(new Function(arg, newFnBody))
}
this.is_a = new Proxy(this, {
get: (target, property, receiver) => {
const newProp = "is_a_" + property
return target[newProp] = true
}
})
this.is_not_a = new Proxy(this, {
get: (target, property, receiver) => {
const newProp = "is_a_" + property
return target[newProp] = false
}
})
this.is_the = new Proxy(this, {
get: (target, property, receiver) => {
target[property] = new Proxy(this, {
get: (nextTarget, nextProp, receiver) => {
nextTarget[property] = nextProp
return nextTarget
}
})
return target[property]
}
})
this.being_the = this.is_the
this.and_the = this.is_the
this.can = new Proxy(this, {
get: (target, property, receiver) => {
target[property] = new Proxy(() => {}, {
apply: (fnTarget, ctx, args) => {
var fn
var past
if(args.length == 1) {
fn = args[0]
} else if (args.length == 2) {
past = args[0]
ctx[past] = []
fn = args[1]
}
var fnStr = fn.toString()
var fnArgs = fnStr.match(/\(?\S+/)[0].split(',')
var nextFn = fnStr.replace(/\${([A-Za-z0-9_]+)}/g, (match, p1) => fnArgs.indexOf(p1) > -1 ? match : '${target.' + p1 + '}')
return ctx[property] = new Proxy(eval(nextFn), {
apply: (nextTarget, nextCtx, nextArgs) => {
var res = nextTarget.apply(nextCtx, nextArgs)
if(nextCtx[past]) {
nextCtx[past].push(res.toString())
}
return res
}
})
}
})
return target[property]
}
})
}
has (num) {
return new Proxy(this, {
get: (target, property, receiver) => {
if(num > 1) {
var thingArray = new Array(num).fill(new Thing(property.replace(/s$/, "")))
target[property] = thingArray
} else {
target[property] = new Thing(property)
}
return target[property]
}
})
}
having(num) {
return this.has(num)
}
}
const jane = new Thing('Jane')
console.log(typeof jane === 'object', 'should be true')
console.log(jane.name, "should be Jane")
jane.is_a.woman
console.log(jane.is_a_woman, "should be true")
console.log(jane.is_a_man, "should be undefined")
jane.is_not_a.man
console.log(jane.is_a_man, "should be false")
jane.is_the.parent_of.joe
console.log(jane.parent_of, "should be joe")
jane.has(4).legs
console.log(jane.legs.length, "should be 4")
jane.has(2).arms
console.log(jane.arms.length, "should be 2")
console.log(jane.arms.every(arm => arm instanceof Thing), "should be true")
console.log(jane.arms[0].name, "should be 'arm'")
jane.has(1).head.having(2).eyes
console.log(jane.head.eyes.length, "should be 2")
console.log(jane.head.eyes[0].name, "should be 'eye'")
const bob = new Thing("Bob")
bob.has(2).hands.each(hand => having(5).fingers)
console.log(bob.hands[0].fingers.length, "should be 5")
console.log(bob.hands[1].fingers.length, "should be 5")
const jack = new Thing('Jack');
jack.has(1).head.having(2).eyes.each(eye => being_the.color.green);
console.log(jack.head.eyes[1].color, "should be green")
const sue = new Thing('Sue')
sue.has(1).head.having(2).eyes.each(eye => being_the.color.blue.and_the.shape.round);
console.log(sue.head.eyes[0].color, "should be blue")
console.log(sue.head.eyes[0].shape, "should be round")
console.log(sue.name, 'should be Sue')
sue.can.speak(phrase => {return `${name} says: ${phrase}!`})
console.log(sue.speak('hello'), 'should be "Sue says: hello!"')
const bill = new Thing('Bill')
bill.can.speak('spoke', phrase => `${name} says: ${phrase}!`)
console.log(bill.speak('hi'), 'should be "Bill says: hi!"')
console.log(bill.spoke, 'should be "Bill says: hi!"')