Created
July 14, 2013 12:46
-
-
Save zaneli/5994163 to your computer and use it in GitHub Desktop.
「CoffeeScript によるデザインパターン(Observer)」ブログ用
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
window.console = | |
log: (msg) -> | |
idName = "__console_log" | |
$("body").append "<textarea id=\"#{idName}\">" if $("##{idName}").size() is 0 | |
$("##{idName}").val($("##{idName}").val() + "#{msg}\n") | |
warn: (msg) -> | |
idName = "__console_warn" | |
$("body").append "<textarea id=\"#{idName}\">" if $("##{idName}").size() is 0 | |
$("##{idName}").val($("##{idName}").val() + "#{msg}\n") | |
# テストケースで配列のindexOfが使用できなかったための回避コード | |
# 参考: http://qiita.com/Oakbow/items/3374175d76d82792134d | |
unless "indexOf" of Array:: | |
Array::indexOf = (find, i) -> | |
i = 0 if i is `undefined` | |
i += @length if i < 0 | |
i = 0 if i < 0 | |
n = @length | |
while i < n | |
return i if i of this and this[i] is find | |
i++ | |
-1 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
mixOf = (base, mixins...) -> | |
class Mixed extends base | |
for mixin in mixins by -1 | |
for name, method of mixin:: | |
Mixed::[name] = method | |
Mixed | |
class Subject | |
constructor: -> @observers = [] | |
notifyObservers: -> observer?.call?(@) for observer in @observers | |
addObserver: (observers...) -> | |
for observer in observers | |
if typeof(observer) is "function" | |
@observers.push observer | |
else | |
console.warn observer?.constructor?.name + " is not a function" | |
deleteObserver: (observers...) -> | |
for observer in observers | |
index = @observers.indexOf(observer) | |
@observers.splice(index, 1) if index >= 0 | |
class @Employee extends (mixOf Subject) | |
constructor: (name, @title, salary) -> | |
super() | |
@getName = -> name | |
@setSalary = (newSalary) -> | |
salary = newSalary | |
@notifyObservers() | |
@getSalary = -> salary | |
@payroll = -> | |
console.log "#{@getName()}のために小切手を切ります!" | |
console.log "彼の給料はいま#{@getSalary()}です!" | |
@taxMan = -> | |
console.log "#{@getName()}に新しい税金の請求書を送ります!" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class @Employee | |
constructor: (name, @title, salary, payroll) -> | |
@getName = -> name | |
@setSalary = (newSalary) -> | |
salary = newSalary | |
payroll.update(@) | |
@getSalary = -> salary | |
class @Payroll | |
update: (changedEmployee) -> | |
console.log "#{changedEmployee.getName()}のために小切手を切ります!" | |
console.log "彼の給料はいま#{changedEmployee.getSalary()}です!" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class @Employee | |
constructor: (name, @title, salary) -> | |
@getName = -> name | |
@setSalary = (newSalary) -> | |
salary = newSalary | |
@notifyObservers() | |
@getSalary = -> salary | |
@observers = [] | |
notifyObservers: -> observer?.update?(@) for observer in @observers | |
addObserver: (observers...) -> | |
for observer in observers | |
if observer?.update? | |
@observers.push observer | |
else | |
console.warn observer?.constructor?.name + " has no method 'update'" | |
deleteObserver: (observers...) -> | |
for observer in observers | |
index = @observers.indexOf(observer) | |
@observers.splice(index, 1) if index >= 0 | |
class @Payroll | |
update: (changedEmployee) -> | |
console.log "#{changedEmployee.getName()}のために小切手を切ります!" | |
console.log "彼の給料はいま#{changedEmployee.getSalary()}です!" | |
class @TaxMan | |
update: (changedEmployee) -> | |
console.log "#{changedEmployee.getName()}に新しい税金の請求書を送ります!" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class @Employee | |
constructor: (name, @title, salary) -> | |
@getName = -> name | |
@setSalary = (newSalary) -> | |
salary = newSalary | |
@notifyObservers() | |
@getSalary = -> salary | |
@observers = [] | |
notifyObservers: -> observer?.call?(@) for observer in @observers | |
addObserver: (observers...) -> | |
for observer in observers | |
if typeof(observer) is "function" | |
@observers.push observer | |
else | |
console.warn observer?.constructor?.name + " is not a function" | |
deleteObserver: (observers...) -> | |
for observer in observers | |
index = @observers.indexOf(observer) | |
@observers.splice(index, 1) if index >= 0 | |
@payroll = -> | |
console.log "#{@getName()}のために小切手を切ります!" | |
console.log "彼の給料はいま#{@getSalary()}です!" | |
@taxMan = -> | |
console.log "#{@getName()}に新しい税金の請求書を送ります!" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Subject | |
constructor: -> @observers = [] | |
notifyObservers: -> observer?.call?(@) for observer in @observers | |
addObserver: (observers...) -> | |
for observer in observers | |
if typeof(observer) is "function" | |
@observers.push observer | |
else | |
console.warn observer?.constructor?.name + " is not a function" | |
deleteObserver: (observers...) -> | |
for observer in observers | |
index = @observers.indexOf(observer) | |
@observers.splice(index, 1) if index >= 0 | |
class @Employee extends Subject | |
constructor: (name, @title, salary) -> | |
super() | |
@getName = -> name | |
@setSalary = (newSalary) -> | |
salary = newSalary | |
@notifyObservers() | |
@getSalary = -> salary | |
@payroll = -> | |
console.log "#{@getName()}のために小切手を切ります!" | |
console.log "彼の給料はいま#{@getSalary()}です!" | |
@taxMan = -> | |
console.log "#{@getName()}に新しい税金の請求書を送ります!" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extend = (obj, mixin) -> | |
obj[name] = method for name, method of mixin | |
obj | |
include = (mixins...) -> | |
extend @.constructor.prototype, mixin for mixin in mixins | |
notifyObserversFunc = notifyObservers: -> | |
if not @observers? then return | |
observer?.call?(@) for observer in @observers | |
addObserverFunc = addObserver: (observers...) -> | |
@observers ?= [] | |
for observer in observers | |
if typeof(observer) is "function" | |
@observers.push observer | |
else | |
console.warn observer?.constructor?.name + " is not a function" | |
deleteObserverFunc = deleteObserver: (observers...) -> | |
if not @observers? then return | |
for observer in observers | |
index = @observers.indexOf(observer) | |
@observers.splice(index, 1) if index >= 0 | |
class @Employee | |
constructor: (name, @title, salary) -> | |
@getName = -> name | |
@setSalary = (newSalary) -> | |
salary = newSalary | |
@notifyObservers() | |
@getSalary = -> salary | |
include.call(@, notifyObserversFunc, addObserverFunc, deleteObserverFunc) | |
@payroll = -> | |
console.log "#{@getName()}のために小切手を切ります!" | |
console.log "彼の給料はいま#{@getSalary()}です!" | |
@taxMan = -> | |
console.log "#{@getName()}に新しい税金の請求書を送ります!" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package views | |
import org.specs2.mutable.Specification | |
import play.api.test.TestServer | |
import play.api.test.Helpers.{ running, HTMLUNIT } | |
class ObserverSpec extends Specification { | |
private val testPort = 3333 | |
"Observer" should { | |
"add payroll observer" in { | |
running(TestServer(testPort), HTMLUNIT) { browser => | |
browser.goTo(s"http://localhost:${testPort}/observer") | |
browser.executeScript(""" | |
var e = new Employee("zaneli", "プログラマ", 100); | |
e.addObserver(payroll); | |
e.setSalary(200); | |
e.setSalary(300); | |
console.log(e.getSalary()); | |
""") | |
browser.$("#__console_log").getValue must_== """zaneliのために小切手を切ります! | |
彼の給料はいま200です! | |
zaneliのために小切手を切ります! | |
彼の給料はいま300です! | |
300 | |
""" | |
browser.$("#__console_warn").getValue must beNull | |
} | |
} | |
"add taxMan observer" in { | |
running(TestServer(testPort), HTMLUNIT) { browser => | |
browser.goTo(s"http://localhost:${testPort}/observer") | |
browser.executeScript(""" | |
var e = new Employee("zaneli", "プログラマ", 100); | |
e.addObserver(taxMan); | |
e.setSalary(200); | |
e.setSalary(300); | |
console.log(e.getSalary()); | |
""") | |
browser.$("#__console_log").getValue must_== """zaneliに新しい税金の請求書を送ります! | |
zaneliに新しい税金の請求書を送ります! | |
300 | |
""" | |
browser.$("#__console_warn").getValue must beNull | |
} | |
} | |
"add payroll and taxMan observers" in { | |
running(TestServer(testPort), HTMLUNIT) { browser => | |
browser.goTo(s"http://localhost:${testPort}/observer") | |
browser.executeScript(""" | |
var e = new Employee("zaneli", "プログラマ", 100); | |
e.addObserver(payroll, taxMan); | |
e.setSalary(200); | |
e.setSalary(300); | |
console.log(e.getSalary()); | |
""") | |
browser.$("#__console_log").getValue must_== """zaneliのために小切手を切ります! | |
彼の給料はいま200です! | |
zaneliに新しい税金の請求書を送ります! | |
zaneliのために小切手を切ります! | |
彼の給料はいま300です! | |
zaneliに新しい税金の請求書を送ります! | |
300 | |
""" | |
browser.$("#__console_warn").getValue must beNull | |
} | |
} | |
"add payroll and taxMan observers, delete taxMan observer" in { | |
running(TestServer(testPort), HTMLUNIT) { browser => | |
browser.goTo(s"http://localhost:${testPort}/observer") | |
browser.executeScript(""" | |
var e = new Employee("zaneli", "プログラマ", 100); | |
e.addObserver(payroll, taxMan); | |
e.setSalary(200); | |
e.deleteObserver(taxMan); | |
e.setSalary(300); | |
console.log(e.getSalary()); | |
""") | |
browser.$("#__console_log").getValue must_== """zaneliのために小切手を切ります! | |
彼の給料はいま200です! | |
zaneliに新しい税金の請求書を送ります! | |
zaneliのために小切手を切ります! | |
彼の給料はいま300です! | |
300 | |
""" | |
browser.$("#__console_warn").getValue must beNull | |
} | |
} | |
"add invalid observers" in { | |
running(TestServer(testPort), HTMLUNIT) { browser => | |
browser.goTo(s"http://localhost:${testPort}/observer") | |
browser.executeScript(""" | |
var e = new Employee("zaneli", "プログラマ", 100); | |
e.addObserver(payroll, taxMan, null, "foo", 1); | |
e.setSalary(200); | |
e.deleteObserver(taxMan); | |
e.setSalary(300); | |
console.log(e.getSalary()); | |
""") | |
browser.$("#__console_log").getValue must_== """zaneliのために小切手を切ります! | |
彼の給料はいま200です! | |
zaneliに新しい税金の請求書を送ります! | |
zaneliのために小切手を切ります! | |
彼の給料はいま300です! | |
300 | |
""" | |
browser.$("#__console_warn").getValue must_== """undefined is not a function | |
String is not a function | |
Number is not a function | |
""" | |
} | |
} | |
// 追加していないオブザーバを削除しても影響が無いことの確認 | |
"delete not added observer" in { | |
running(TestServer(testPort), HTMLUNIT) { browser => | |
browser.goTo(s"http://localhost:${testPort}/observer") | |
browser.executeScript(""" | |
var e = new Employee("zaneli", "プログラマ", 100); | |
e.addObserver(payroll); | |
e.setSalary(200); | |
e.deleteObserver(taxMan); | |
e.setSalary(300); | |
console.log(e.getSalary()); | |
""") | |
browser.$("#__console_log").getValue must_== """zaneliのために小切手を切ります! | |
彼の給料はいま200です! | |
zaneliのために小切手を切ります! | |
彼の給料はいま300です! | |
300 | |
""" | |
browser.$("#__console_warn").getValue must beNull | |
} | |
} | |
// addObserver ではなく observers = で直接オブザーバを上書きして不正なオブザーバを設定してもエラーにならないことの確認 | |
"rewrite observers" in { | |
running(TestServer(testPort), HTMLUNIT) { browser => | |
browser.goTo(s"http://localhost:${testPort}/observer") | |
browser.executeScript(""" | |
var e = new Employee("zaneli", "プログラマ", 100); | |
e.addObserver(payroll); | |
e.observers = [taxMan, null, "foo", 1] | |
e.setSalary(200); | |
e.deleteObserver(taxMan); | |
e.setSalary(300); | |
console.log(e.getSalary()); | |
""") | |
browser.$("#__console_log").getValue must_== """zaneliに新しい税金の請求書を送ります! | |
300 | |
""" | |
browser.$("#__console_warn").getValue must beNull | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment