Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

all but share/keyboard tests passing

  • Loading branch information...
commit 67b1103272c596532b84bdd8b80755b7c9d5ce59 1 parent ba3eca6
@collin authored
Showing with 1,669 additions and 513 deletions.
  1. +22 −5 Assetfile
  2. +5 −0 Gemfile
  3. +31 −1 Gemfile.lock
  4. +7 −188 alpha_simprini.sublime-workspace
  5. +5 −3 src/alpha_simprini.coffee
  6. +1 −1  src/alpha_simprini/client/binding/check_box.coffee
  7. +1 −1  src/alpha_simprini/client/binding/file.coffee
  8. +72 −47 src/alpha_simprini/client/binding/model.coffee
  9. +1 −1  src/alpha_simprini/client/models/targets.coffee
  10. +4 −1 src/alpha_simprini/client/view.coffee
  11. +1 −1  src/alpha_simprini/client/views/dialog.coffee
  12. +3 −2 src/alpha_simprini/core/model/local.coffee
  13. +1 −1  src/alpha_simprini/core/model/share.coffee
  14. +0 −1  src/alpha_simprini/core/model/store.coffee
  15. +3 −4 src/alpha_simprini/core/properties/belongs_to.coffee
  16. +31 −35 src/alpha_simprini/core/properties/field.coffee
  17. +2 −1  src/alpha_simprini/core/properties/has_many.coffee
  18. +12 −11 src/alpha_simprini/core/properties/has_one.coffee
  19. +1 −1  src/alpha_simprini/css/views/angle_picker.coffee
  20. +1 −1  src/alpha_simprini/css/views/color_picker.coffee
  21. +2 −2 src/alpha_simprini/css/views/color_stop_picker.coffee
  22. +37 −0 src/dev-channel.coffee
  23. +93 −0 src/dev-channel.rb
  24. +5 −0 test/client.js
  25. +54 −0 test/client/application.js
  26. +26 −0 test/client/binding.js
  27. +2 −5 test/client/binding/model.coffee
  28. +74 −0 test/client/binding_group.js
  29. +42 −0 test/client/dom.js
  30. +17 −13 test/client/models/targets.coffee
  31. +269 −0 test/client/view.js
  32. +57 −0 test/client/view_events.js
  33. +2 −2 test/client/view_model.coffee
  34. +43 −0 test/client/view_model.js
  35. +5 −0 test/client_helper.js
  36. +2 −2 test/core.coffee
  37. +57 −0 test/core.js
  38. +36 −0 test/core/callbacks.js
  39. +149 −0 test/core/collection.js
  40. +136 −0 test/core/filtered_collection.js
  41. +42 −0 test/core/instance_methods.js
  42. +5 −0 test/core/logging.js
  43. +21 −0 test/core/model.js
  44. +50 −50 test/core/properties/belongs_to.coffee
  45. +50 −50 test/core/properties/field.coffee
  46. +79 −79 test/core/properties/has_many.coffee
  47. +2 −2 test/core/properties/virtual_property.coffee
  48. +2 −1  test/core/state_machine.coffee
  49. 0  test/core/state_machine.js
  50. +3 −1 test/helper.coffee
  51. +103 −0 test/helper.js
View
27 Assetfile
@@ -1,11 +1,20 @@
require "rake-pipeline-web-filters"
+require "pathology-rakep"
require "json"
# require "uglifier"
+output "lib"
+input "src" do
+ match "**/*.coffee" do |input|
+ filter PathologyConstantFilter
+ coffee_script
+ end
+end
+
output "dist"
input "lib" do
match "**/*.js" do
- minispade rewrite_requires: true, module_id_generator: proc { |input|
+ minispade rewrite_requires: true, string:true, module_id_generator: proc { |input|
id = input.path.dup
id.sub!('/lib/', '/')
id.sub!(/\.js$/, '')
@@ -17,9 +26,17 @@ input "lib" do
end
end
-input "./", "alpha_simprini.erb" do
- require "./version"
- filter(Rake::Pipeline::Web::Filters::TiltFilter, {}, Object) do |input|
- "alpha_simprini.html"
+output "tmp/test"
+input "test" do
+ match "**/*.coffee" do
+ filter PathologyConstantFilter
+ coffee_script
end
end
+# input "./", "alpha_simprini.erb" do
+# require "./version"
+
+# filter(Rake::Pipeline::Web::Filters::TiltFilter, {}, Object) do |input|
+# "alpha_simprini.html"
+# end
+# end
View
5 Gemfile
@@ -4,6 +4,11 @@ source "https://rubygems.org"
gem "rake-pipeline", :git => "https://github.com/livingsocial/rake-pipeline.git"
gem "rake-pipeline-web-filters", :git => "https://github.com/wycats/rake-pipeline-web-filters.git"
gem "pathology-rake", git: "git://gist.github.com/4194390.git"
+gem "reel"
+gem "celluloid"
+gem "color"
+gem "listen"
+gem 'rb-fsevent', '~> 0.9.1'
gem "colored"
gem "uglifier", "~> 1.0.3"
gem "coffee-script"
View
32 Gemfile.lock
@@ -1,6 +1,6 @@
GIT
remote: git://gist.github.com/4194390.git
- revision: 58b17c2276b7b4df6e6743f408df651d5db49184
+ revision: 6f2a54b654f47b18605126c56219057d21f4f5e1
specs:
pathology-rake (0.2.0)
@@ -34,13 +34,22 @@ GEM
remote: https://rubygems.org/
specs:
addressable (2.2.7)
+ celluloid (0.12.3)
+ facter (>= 1.6.12)
+ timers (>= 1.0.0)
+ celluloid-io (0.12.0)
+ celluloid (~> 0.12.0)
+ nio4r (>= 0.4.0)
+ certified (0.1.1)
coffee-script (2.2.0)
coffee-script-source
execjs
coffee-script-source (1.2.0)
+ color (1.4.1)
colored (1.2)
execjs (1.3.0)
multi_json (~> 1.0)
+ facter (1.6.16)
faraday (0.7.6)
addressable (~> 2.2)
multipart-post (~> 1.1)
@@ -55,36 +64,57 @@ GEM
github_api (= 0.4.11)
rest-client
hashie (1.2.0)
+ http (0.4.0)
+ certified
+ http_parser.rb
+ http_parser.rb (0.5.3)
+ listen (0.6.0)
mime-types (1.18)
multi_json (1.3.2)
multipart-post (1.1.5)
+ nio4r (0.4.3)
nokogiri (1.5.5)
oauth2 (0.5.2)
faraday (~> 0.7)
multi_json (~> 1.0)
rack (1.4.1)
rake (0.9.2.2)
+ rb-fsevent (0.9.2)
+ reel (0.2.0)
+ celluloid-io (>= 0.8.0)
+ http (>= 0.2.0)
+ http_parser.rb (>= 0.5.3)
+ rack (>= 1.4.0)
+ websocket_parser (>= 0.1.0)
rest-client (1.6.7)
mime-types (>= 1.16)
thor (0.16.0)
tilt (1.3.3)
+ timers (1.0.1)
uglifier (1.0.4)
execjs (>= 0.3.0)
multi_json (>= 1.0.2)
watchr (0.7)
+ websocket_parser (0.1.1)
+ http
PLATFORMS
ruby
DEPENDENCIES
+ celluloid
coffee-script
+ color
colored
github_uploader (~> 0.1.0)
html_package!
+ listen
pathology-rake!
rake
rake-pipeline!
rake-pipeline-web-filters!
+ rb-fsevent (~> 0.9.1)
+ reel
tilt
uglifier (~> 1.0.3)
watchr
View
195 alpha_simprini.sublime-workspace
@@ -519,46 +519,6 @@
},
"buffers":
[
- {
- "file": "src/doc.coffee",
- "settings":
- {
- "buffer_size": 2630,
- "line_ending": "Unix"
- }
- },
- {
- "file": "docs/index.html",
- "settings":
- {
- "buffer_size": 2587,
- "line_ending": "Unix"
- }
- },
- {
- "file": "test/core/callbacks.coffee",
- "settings":
- {
- "buffer_size": 452,
- "line_ending": "Unix"
- }
- },
- {
- "file": "src/alpha_simprini/core/callbacks.coffee",
- "settings":
- {
- "buffer_size": 706,
- "line_ending": "Unix"
- }
- },
- {
- "file": "Rakefile",
- "settings":
- {
- "buffer_size": 3970,
- "line_ending": "Unix"
- }
- }
],
"build_system": "",
"command_palette":
@@ -600,6 +560,11 @@
},
"file_history":
[
+ "/Users/collin/Code/alpha_simprini/Rakefile",
+ "/Users/collin/Code/alpha_simprini/src/alpha_simprini/core/callbacks.coffee",
+ "/Users/collin/Code/alpha_simprini/test/core/callbacks.coffee",
+ "/Users/collin/Code/alpha_simprini/docs/index.html",
+ "/Users/collin/Code/alpha_simprini/src/doc.coffee",
"/Users/collin/Code/cms/public/javascripts/application.js",
"/Users/collin/Code/alpha_simprini/test/core/properties/has_many.coffee",
"/Users/collin/Code/alpha_simprini/src/alpha_simprini/core/model/share.coffee",
@@ -609,7 +574,6 @@
"/Users/collin/Code/alpha_simprini/src/alpha_simprini/client/binding/if.coffee",
"/Users/collin/Code/alpha_simprini/src/alpha_simprini/core/properties/field.coffee",
"/Users/collin/Code/alpha_simprini/Assetfile",
- "/Users/collin/Code/alpha_simprini/Rakefile",
"/Users/collin/Code/alpha_simprini/src/alpha_simprini/core/filtered_collection.coffee",
"/Users/collin/Code/alpha_simprini/src/alpha_simprini.coffee",
"/Users/collin/Code/alpha_simprini/src/alpha_simprini/core/model.coffee",
@@ -624,12 +588,10 @@
"/Users/collin/Code/alpha_simprini/src/alpha_simprini/client/application.coffee",
"/Users/collin/Code/alpha_simprini/src/alpha_simprini/client.coffee",
"/Users/collin/Code/alpha_simprini/test/index.html",
- "/Users/collin/Code/alpha_simprini/docs/index.html",
"/Users/collin/Code/alpha_simprini/Assetfile-docs",
"/Users/collin/Code/alpha_simprini/test/client/dom.coffee",
"/Users/collin/Code/alpha_simprini/src/alpha_simprini/client/dom.coffee",
"/Users/collin/Code/alpha_simprini/autobuild.watchr",
- "/Users/collin/Code/alpha_simprini/src/doc.coffee",
"/Users/collin/Code/alpha_simprini/test/client.coffee",
"/Users/collin/Code/alpha_simprini/docs/doc.js",
"/Users/collin/Code/alpha_simprini/src/alpha_simprini/core/properties/has_many.coffee",
@@ -725,9 +687,7 @@
"/Users/collin/Code/alpha_simprini/src/alpha_simprini/color.coffee",
"/Users/collin/Code/alpha_simprini/src/alpha_simprini/color/models/color_stops.coffee",
"/Users/collin/Code/alpha_simprini/src/alpha_simprini/color/views/color_stop_picker.coffee",
- "/Users/collin/Code/alpha_simprini/src/alpha_simprini/color/models/color_stop.coffee",
- "/Users/collin/Code/alpha_simprini/lib/alpha_simprini/client/binding/edit_line.js",
- "/Users/collin/Code/alpha_simprini/src/alpha_simprini/core/logging.coffee"
+ "/Users/collin/Code/alpha_simprini/src/alpha_simprini/color/models/color_stop.coffee"
],
"find":
{
@@ -735,7 +695,7 @@
},
"find_in_files":
{
- "height": 93.0,
+ "height": 0.0,
"where_history":
[
"*.coffee,/Users/collin/Code/alpha_simprini/src",
@@ -1007,149 +967,8 @@
"groups":
[
{
- "selected": 0,
"sheets":
[
- {
- "buffer": 0,
- "file": "src/doc.coffee",
- "settings":
- {
- "buffer_size": 2630,
- "regions":
- {
- },
- "selection":
- [
- [
- 767,
- 767
- ]
- ],
- "settings":
- {
- "syntax": "Packages/CofeeScript/CoffeeScript.tmLanguage",
- "tab_size": 2,
- "translate_tabs_to_spaces": true
- },
- "translation.x": 0.0,
- "translation.y": 0.0,
- "zoom_level": 1.0
- },
- "type": "text"
- },
- {
- "buffer": 1,
- "file": "docs/index.html",
- "settings":
- {
- "buffer_size": 2587,
- "regions":
- {
- },
- "selection":
- [
- [
- 2245,
- 2245
- ]
- ],
- "settings":
- {
- "syntax": "Packages/HTML/HTML.tmLanguage",
- "tab_size": 2,
- "translate_tabs_to_spaces": true
- },
- "translation.x": 0.0,
- "translation.y": 1031.0,
- "zoom_level": 1.0
- },
- "type": "text"
- },
- {
- "buffer": 2,
- "file": "test/core/callbacks.coffee",
- "settings":
- {
- "buffer_size": 452,
- "regions":
- {
- },
- "selection":
- [
- [
- 0,
- 0
- ]
- ],
- "settings":
- {
- "syntax": "Packages/CofeeScript/CoffeeScript.tmLanguage",
- "tab_size": 2,
- "translate_tabs_to_spaces": true
- },
- "translation.x": 0.0,
- "translation.y": 0.0,
- "zoom_level": 1.0
- },
- "type": "text"
- },
- {
- "buffer": 3,
- "file": "src/alpha_simprini/core/callbacks.coffee",
- "settings":
- {
- "buffer_size": 706,
- "regions":
- {
- },
- "selection":
- [
- [
- 232,
- 232
- ]
- ],
- "settings":
- {
- "syntax": "Packages/CofeeScript/CoffeeScript.tmLanguage",
- "tab_size": 2,
- "translate_tabs_to_spaces": true
- },
- "translation.x": 0.0,
- "translation.y": 0.0,
- "zoom_level": 1.0
- },
- "type": "text"
- },
- {
- "buffer": 4,
- "file": "Rakefile",
- "settings":
- {
- "buffer_size": 3970,
- "regions":
- {
- },
- "selection":
- [
- [
- 2033,
- 2033
- ]
- ],
- "settings":
- {
- "syntax": "Packages/Ruby/Ruby.tmLanguage",
- "tab_size": 2,
- "translate_tabs_to_spaces": true
- },
- "translation.x": 0.0,
- "translation.y": 843.0,
- "zoom_level": 1.0
- },
- "type": "text"
- }
]
}
],
View
8 src/alpha_simprini.coffee
@@ -3,7 +3,8 @@ require "pathology"
require "taxi"
require "fleck"
-module AlphaSimprini
+AlphaSimprini = Pathology.Namespace.new("AlphaSimprini")
+
window.AS = AlphaSimprini
require "alpha_simprini/core/string"
@@ -18,10 +19,11 @@ AS.part = (name) ->
AS[name] = require: (libraries) -> AS.require name.toLowerCase(), libraries
# Namespaces
-module AS.Models
-module AS.Views
+AS._module("Models")
+AS._module("Views")
+AS.Namespace = Pathology.Namespace
AS.Object = Pathology.Object
AS.Map = Pathology.Map
AS.Module = Pathology.Module
View
2  src/alpha_simprini/client/binding/check_box.coffee
@@ -1,4 +1,4 @@
-class AS.Binding.CheckBok < AS.Binding.Input
+class AS.Binding.CheckBox < AS.Binding.Input
def initialize: (context, model, field, options={}, fn=undefined) ->
options.type = "checkbox"
@_super.apply(this, arguments)
View
2  src/alpha_simprini/client/binding/file.coffee
@@ -1,4 +1,4 @@
-class As.Binding.File < AS.Binding.Input
+class AS.Binding.File < AS.Binding.Input
def makeContent: ->
options = _.clone(@options)
options.type = "file"
View
119 src/alpha_simprini/client/binding/model.coffee
@@ -11,19 +11,33 @@ class AS.Binding.Model < AS.Binding
# """
def css: (properties) ->
- for property, options of properties
- do (property, options) =>
- if _.isArray(options)
- @styles[property] = => @model.readPath(options)
- painter = => _.defer =>
- value = @styles[property]()
- @content.css property, value
-
- @context.binds @model, options, painter, this
- else
- @styles[property] = => options.fn(@model)
- painter = => _.defer => @content.css property, @styles[property]()
- @context.binds @model, options.field, painter, this
+ for property, path of properties
+ do (property, path) =>
+ @styles[property] = => @model.readPath(path)
+ painter = =>
+ value = @styles[property]()
+ @content.css property, value
+
+ @context.binds @model, path, painter, this
+
+ _.defer => @paint()
+
+
+ # for property, options of properties
+ # do (property, options) =>
+ # if _.isArray(options)
+ # @styles[property] = => @model.readPath(options)
+ # painter = => _.defer =>
+ # value = @styles[property]()
+ # @content.css property, value
+
+ # @context.binds @model, options, painter, this
+ # else
+ # @styles[property] = =>
+ # console.log "PAINTER"
+ # options.fn(@model)
+ # painter = => _.defer => @content.css property, @styles[property]()
+ # @context.binds @model, options.field, painter, this
# @::css.doc =
# params: [
# []
@@ -33,40 +47,51 @@ class AS.Binding.Model < AS.Binding
# """
def attr: (attrs) ->
- for property, options of attrs
- do (property, options) =>
- if _.isArray(options)
- @attrs[property] = =>
- value = @model.readPath(options)
- if value
- "yes"
- else if value in [false, null, undefined]
- "no"
- else
- value
-
- painter = => _.defer =>
- @content.attr property, @attrs[property]()
-
- bindingPath = options
- @context.binds @model, bindingPath, painter, this
- else
- @attrs[property] = =>
- if options.fn
- options.fn(@model)
- else
- value = @model[options.field].get()
- if value
- "yes"
- else if value in [false, null, undefined]
- "no"
- else
- value
-
- painter = => _.defer =>
- @content.attr property, @attrs[property]()
-
- @context.binds @model, options.field, painter, this
+ for property, path of attrs
+ do (property, path) =>
+ @attrs[property] = => @model.readPath(path)
+ painter = =>
+ value = @attrs[property]()
+ @content.attr property, value
+
+ @context.binds @model, path, painter, this
+
+ _.defer => @paint()
+
+ # for property, options of attrs
+ # do (property, options) =>
+ # if _.isArray(options)
+ # @attrs[property] = =>
+ # value = @model.readPath(options)
+ # if value
+ # "yes"
+ # else if value in [false, null, undefined]
+ # "no"
+ # else
+ # value
+
+ # painter = => _.defer =>
+ # @content.attr property, @attrs[property]()
+
+ # bindingPath = options
+ # @context.binds @model, bindingPath, painter, this
+ # else
+ # @attrs[property] = =>
+ # if options.fn
+ # options.fn(@model)
+ # else
+ # value = @model[options.field].get()
+ # if value
+ # "yes"
+ # else if value in [false, null, undefined]
+ # "no"
+ # else
+ # value
+
+ # painter = => _.defer =>
+ # @content.attr property, @attrs[property]()
+
+ # @context.binds @model, options.field, painter, this
# @::attr.doc =
# params: [
# []
View
2  src/alpha_simprini/client/models/targets.coffee
@@ -180,7 +180,7 @@ class AS.Models.Targets.Edge < AS.Models.Targets
# """
-
+class AS.Models.Targets.Thirds < AS.Models.Targets
def withinVertically: (y, rect) ->
rect.top <= y <= rect.bottom
View
5 src/alpha_simprini/client/view.coffee
@@ -1,4 +1,7 @@
-class AS.View
+AS.part("Core")
+AS.Core.require("callbacks")
+
+class AS.View < AS.DOM
include Taxi.Mixin
include AS.Callbacks
View
2  src/alpha_simprini/client/views/dialog.coffee
@@ -2,7 +2,7 @@ require "knead"
module AS.Views.Dialogs
-class As.Views.Dialog < As.Views.Panel
+class AS.Views.Dialog < AS.Views.Panel
@afterContent (view) -> knead.monitor view.head
def initialize: ->
View
5 src/alpha_simprini/core/model/local.coffee
@@ -1,5 +1,6 @@
-# {bind, flatten, each} = _
-# {upperCamelize} = fleck
+{bind, flatten, each} = _
+{upperCamelize} = fleck
+
# AS.Model.LocalStorageAdapter = AS.Object.extend ({delegate, include, def, defs}) ->
# def localStorage: window.localStorage
View
2  src/alpha_simprini/core/model/share.coffee
@@ -20,7 +20,7 @@ getConnection = (origin) ->
connections[origin]
-class AS.Model.ShareJSAdapter
+class AS.Models.ShareJSAdapter
include Taxi.Mixin
# delegate 'open', to: 'store'
View
1  src/alpha_simprini/core/model/store.coffee
@@ -1,6 +1,5 @@
{extend, clone} = _
-
class AS.Model.Store
include Taxi.Mixin
View
7 src/alpha_simprini/core/properties/belongs_to.coffee
@@ -1,5 +1,5 @@
class AS.Model.BelongsTo < AS.Model.HasOne
-class AS.Model.HasOne.Instance < As.Model.HasOne.Instance
+class AS.Model.BelongsTo.Instance < AS.Model.HasOne.Instance
def bindToValue: (value) ->
@_super.apply(this, arguments)
value.bind "destroy#{@namespace}", =>
@@ -8,15 +8,14 @@ class AS.Model.HasOne.Instance < As.Model.HasOne.Instance
else
@set(null)
-class AS.Model.HasOne.Instance.Synapse < AS.Model.Field.Instance.Synapse
+class AS.Model.BelongsTo.Instance.Synapse < AS.Model.Field.Instance.Synapse
def get: ->
@raw.get()
def set: (value) ->
@raw.set(value)
-
-class class AS.Model.HasOne.Instance.ShareSynapse < AS.Model.Field.Instance.ShareSynapse
+class AS.Model.BelongsTo.Instance.ShareSynapse < AS.Model.Field.Instance.ShareSynapse
def get: ->
@raw.at(@path).get()
View
66 src/alpha_simprini/core/properties/field.coffee
@@ -2,7 +2,6 @@
# TODO: Field is generic. reuse it.
-
class AS.Model.Enum
defs read: (value, options) ->
# AS.Assert options.values
@@ -12,7 +11,6 @@ class AS.Model.Enum
# AS.Assert options.values
options.values.indexOf(value)
-
class AS.Model.String
defs read: (value) ->
String(value) if value?
@@ -53,9 +51,7 @@ class AS.Model.TokenList
defs write: (value) ->
value.join(",")
-
-
-class AS.Model.Field
+class AS.Model.Field < AS.Property
defs Casters: AS.Map.new()
Casters = @Casters
@@ -106,38 +102,38 @@ class AS.Model.Field.Instance < AS.Property.Instance
#
# """
- def syncWith: (share) ->
- return if @options.remote is false
- @share = share.at(@options.name)
- @set shareValue if shareValue = @share.get()
- # #PERF console.time("set")
- # @share.set("") unless @share.get()?
- # #PERF console.timeEnd("set")
- @stopSync()
-
- @synapse = @constructor.Synapse.new(this)
- @shareSynapse = @constructor.ShareSynapse.new(share, @options.name)
-
- @synapse.notify(@shareSynapse)
- # @::syncWith.doc =
- # params: [
- # ["share", "ShareJS.Doc", true]
- # ]
- # desc: """
- #
- # """
-
- def stopSync: ->
- # @synapse?.stopObserving()
- @synapse?.stopNotifying()
- # @::stopSync.doc =
- # desc: """
- #
- # """
+ # def syncWith: (share) ->
+ # return if @options.remote is false
+ # @share = share.at(@options.name)
+ # @set shareValue if shareValue = @share.get()
+ # # #PERF console.time("set")
+ # # @share.set("") unless @share.get()?
+ # # #PERF console.timeEnd("set")
+ # @stopSync()
+
+ # @synapse = @constructor.Synapse.new(this)
+ # @shareSynapse = @constructor.ShareSynapse.new(share, @options.name)
+
+ # @synapse.notify(@shareSynapse)
+ # # @::syncWith.doc =
+ # # params: [
+ # # ["share", "ShareJS.Doc", true]
+ # # ]
+ # # desc: """
+ # #
+ # # """
+
+ # def stopSync: ->
+ # # @synapse?.stopObserving()
+ # @synapse?.stopNotifying()
+ # # @::stopSync.doc =
+ # # desc: """
+ # #
+ # # """
def get: ->
if @value isnt undefined
- value = Casters.get(@options.type).read(@value, @options)
+ value = AS.Model.Field.Casters.get(@options.type).read(@value, @options)
else
@options.default
# @::get.doc =
@@ -147,7 +143,7 @@ class AS.Model.Field.Instance < AS.Property.Instance
# """
def set: (value) ->
- writeValue = Casters.get(@options.type).write(value, @options)
+ writeValue = AS.Model.Field.Casters.get(@options.type).write(value, @options)
return @value if writeValue is @value
@value = writeValue
@object.trigger("change")
View
3  src/alpha_simprini/core/properties/has_many.coffee
@@ -5,6 +5,7 @@ class AS.Model.HasMany < AS.Model.Field
return true if @options.model?() in (test.ancestors or [])
@_super.apply(this, arguments)
+class AS.Model.HasMany.Instance < AS.Model.Field.Instance
delegate AS.COLLECTION_DELEGATES, to: "backingCollection"
delegate 'groupBy', 'bind', 'trigger', 'unbind', to: "backingCollection"
@@ -66,7 +67,7 @@ class AS.Model.HasMany < AS.Model.Field
# """
def objects: (test) ->
- if include test?.ancestors, AS.Model
+ if _.include test?.ancestors, AS.Model
model for model in @backingCollection.models.value() when model instanceof test
else
@backingCollection.models.value()
View
23 src/alpha_simprini/core/properties/has_one.coffee
@@ -1,4 +1,4 @@
-AS.Model.HasOne < As.Model.Field
+class AS.Model.HasOne < AS.Model.Field
def couldBe: (test) ->
return true if test in @options.model?().ancestors
@_super.apply(this, arguments)
@@ -10,6 +10,7 @@ AS.Model.HasOne < As.Model.Field
#
# """
+class AS.Model.HasOne.Instance < AS.Model.Field.Instance
def initialize: (@object, @options) ->
@options.model ?= -> AS.Model
@model = @options.model
@@ -82,19 +83,19 @@ AS.Model.HasOne < As.Model.Field
value.bind "change#{@namespace}", => @triggerDependants()
value.bind "destroy#{@namespace}", => @set(null)
- @Synapse = AS.Model.Field.Instance.Synapse.extend ({delegate, include, def, defs}) ->
- def get: ->
- @raw.get()
+class AS.Model.HasOne.Instance.Synapse < AS.Model.Synapse
+ def get: ->
+ @raw.get()
- def set: (value) ->
- @raw.set(value)
+ def set: (value) ->
+ @raw.set(value)
- @ShareSynapse = AS.Model.Field.Instance.ShareSynapse.extend ({delegate, include, def, defs}) ->
- def get: ->
- @raw.at(@path).get()
+class AS.Model.HasOne.Instance.Synapse < AS.Model.Field.Instance.ShareSynapse
+ def get: ->
+ @raw.at(@path).get()
- def set: (value) ->
- @_super(value?.id) if value?.id
+ def set: (value) ->
+ @_super(value?.id) if value?.id
AS.Model.defs hasOne: (name, options) ->
AS.Model.HasOne.new(name, this, options)
View
2  src/alpha_simprini/css/views/angle_picker.coffee
@@ -1,4 +1,4 @@
-class AnglePicker < AS.View
+class AS.Views.AnglePicker < AS.View
@afterContent (view) ->
knead.monitor view.el
View
2  src/alpha_simprini/css/views/color_picker.coffee
@@ -1,4 +1,4 @@
-class colorPicker
+class AS.Views.ColorPicker < AS.View
def tagName: 'img'
def events:
View
4 src/alpha_simprini/css/views/color_stop_picker.coffee
@@ -1,7 +1,7 @@
require "knead"
{defer} = _
-class ColorStopHandle < AS.View
+class AS.Views.ColorStopHandle < AS.View
def events:
"knead:dragstart": "dragstartStop"
"knead:drag": "dragStop"
@@ -34,7 +34,7 @@ class ColorStopHandle < AS.View
@colorPicker.pick @model
-class ColorStopPicker < AS.View
+class AS.Views.ColorStopPicker < AS.View
@afterContent (view) ->
knead.monitor view.el
View
37 src/dev-channel.coffee
@@ -0,0 +1,37 @@
+{bind} = _
+class DevChannel
+ def initialize: (@host, @port, Socket=WebSocket) ->
+ @connect()
+
+ def connect: ->
+ @socket?.close()
+ @socket = Socket.new("ws://#{@host}:#{port}")
+ @socket.onmessage = bind(@onmessage, this)
+ @socket.onclose = bind(@onclose, this)
+ @socket.onerror = bind(@onclose, this)
+
+ def onmessage: ({data}) ->
+ @trampoline = undefined
+ if data.protocol
+ @[data.protocol].apply(this, data.arguments)
+
+ def onclose: (event) ->
+ @trampoline ||= 1
+ if @trampoline
+ console.log "Attempting reconnect #{@trampoline}/3"
+ @trampoline++
+
+ if @trampoline < 3
+ @connect()
+ else
+ console.error "Unexpectedly Closed DevChannel", event
+ console.info "Reason Unknown"
+ console.log "reload page to reconnect"
+
+ def loadStylesheet: (path, source) ->
+
+ def loadScript: (path, source) ->
+ console.log "loadScript", path, source
+ minispade.modules[path] = source
+ minispade.flushCache()
+
View
93 src/dev-channel.rb
@@ -0,0 +1,93 @@
+require 'rubygems'
+require 'listen'
+require 'bundler/setup'
+require 'reel'
+require 'rake-pipeline'
+
+module Pathology
+end
+
+class Pathology::Engine < Rails::Engine
+ def self.add_project(assetfile)
+ Celluloid::Actor[:asset_change_server].add_project(assetfile)
+ end
+
+ initializer "pathology.asset_change_server" do
+ Pathology::AssetChangeServer.supervise_as :asset_change_server
+ end
+end
+
+class Pathology::AssetChangeServer
+ include Celluloid
+ include Celluloid::Notifications
+ include Celluloid::Logger
+
+ def add_project(assetfile)
+ project = Rake::Pipeline::Project.new(assetfile)
+ project.pipelines.map(&:inputs).each do |map|
+ next unless map.key?("src")
+ path = "./src/#{map["src"].gsub!('*', '')}"
+ path.gsub! "//", "/"
+ end
+
+ listener = Listen.to().change do |modified, added, removed|
+ modified.each do |path|
+ changed(path)
+ end
+ end
+ listener.start(false)
+ end
+
+ def changed(path)
+ publish 'file_change', path
+ end
+end
+
+class Pathology::DevChannel
+ include Celluloid
+ include Celluloid::Notifications
+ include Celluloid::Logger
+
+ include Celluloid::Instrumentation
+
+ def initialize(socket)
+ info "Streaming DevChannel updates to client #{socket}"
+ @socket = socket
+ subscribe('file_change', :notify_file_change)
+ end
+
+ def notify_file_change(topic, path)
+ instrument :FileChange
+ new_path = "tmp/outfile.js"
+ project = Rake::Pipeline::Project.build do
+ tmpdir "tmp"
+ output "tmp"
+
+ input File.dirname(path) do
+ match File.basename(path) do
+ concat new_path
+ end
+ end
+ end
+
+ # client_path = new_path.gsub /.*lib\//, ''
+
+ # @socket << JSON.dump({protocol: "loadScript", args: [client_path, File.read(new_path)]})
+ end_instrument :FileChange
+ rescue Reel::SocketError
+ info "AssetChangeServer client disconnected"
+ terminate
+ end
+
+ def instrument(id)
+ @active ||= {}
+ @active[id] = Time.new
+ end
+
+ def end_instrument(id)
+ started_at = @active.delete(id)
+ total = ((Time.new - started_at) * 1000.0).to_s
+ info "[#{id}] #{total}ms"
+ end
+end
+
View
5 test/client.js
@@ -0,0 +1,5 @@
+(function() {
+
+
+
+}).call(this);
View
54 test/client/application.js
@@ -0,0 +1,54 @@
+(function() {
+
+ module("Application", {
+ setup: function() {
+ return this.app = AS.Application["new"]({
+ el: this.el = $("<div>")
+ });
+ }
+ });
+
+ test("attaches global key handlers w/jwerty", function() {
+ var app, event, events, trigger, triggers, _fn, _i, _j, _len, _len2, _results,
+ _this = this;
+ events = ["open", "up", "down", "first", "last", "left", "right", "indent", "dedent", "alphanum", "escape", "accept", "delete"];
+ app = this.app;
+ _fn = function(event) {
+ return app.bind(event, function(_event) {
+ return ok(_event, "handled " + event);
+ });
+ };
+ for (_i = 0, _len = events.length; _i < _len; _i++) {
+ event = events[_i];
+ _fn(event);
+ }
+ triggers = ["esc", "cmd+enter", "backspace", "enter", "up", "down", "home", "end", "left", "right", "tab", "shift+tab", "a", "b", "C", "D", "1", "2"];
+ expect(triggers.length);
+ _results = [];
+ for (_j = 0, _len2 = triggers.length; _j < _len2; _j++) {
+ trigger = triggers[_j];
+ jwerty.fire(trigger, this.el);
+ _results.push(Taxi.Governer.exit());
+ }
+ return _results;
+ });
+
+ test("initializes views into the application context", function() {
+ var app_panel;
+ app_panel = this.app.view(AS.Views.Panel, {
+ key: "value"
+ });
+ equal(app_panel.application, this.app);
+ return equal(app_panel.key, "value");
+ });
+
+ test("appends views into the app dom element", function() {
+ var app_panel;
+ app_panel = this.app.view(AS.Views.Panel, {
+ key: "value"
+ });
+ this.app.append(app_panel);
+ return equal(this.app.el.children()[0], app_panel.el[0]);
+ });
+
+}).call(this);
View
26 test/client/binding.js
@@ -0,0 +1,26 @@
+(function() {
+ var mockBinding;
+
+ mockBinding = NS.mockBinding;
+
+ module("Binding");
+
+ test("stashes the binding container", function() {
+ var binding, mocks, _ref;
+ _ref = mockBinding(AS.Binding), mocks = _ref[0], binding = _ref[1];
+ return equal(binding.container[0], binding.context.currentNode);
+ });
+
+ test("stashes the binding group", function() {
+ var binding, mocks, _ref;
+ _ref = mockBinding(AS.Binding), mocks = _ref[0], binding = _ref[1];
+ return equal(binding.bindingGroup, binding.context.bindingGroup);
+ });
+
+ test("gets the field value", function() {
+ var binding, mocks, _ref;
+ _ref = mockBinding(AS.Binding), mocks = _ref[0], binding = _ref[1];
+ return equal(binding.fieldValue(), "value");
+ });
+
+}).call(this);
View
7 test/client/binding/model.coffee
@@ -16,7 +16,6 @@ test "paints styles", ->
binding.css
"background-color": ["field1"]
-
binding.paint()
equal content.css("background-color"), "rgb(34, 34, 34)"
@@ -24,16 +23,14 @@ test "paints styles", ->
Taxi.Governer.exit()
equal content.css("background-color"), "rgb(0, 0, 0)"
-
test "paints attributes", ->
context = AS.View.new()
content = $("<div>")
model = BM.Model.new()
+ model.field1.set("mock-value")
binding = AS.Binding.Model.new context, model, content
binding.attr
- "data-property":
- fn: (model) -> model.property or "mock-value"
- field: ["field1"]
+ "data-property": ["field1"]
binding.paint()
View
74 test/client/binding_group.js
@@ -0,0 +1,74 @@
+(function() {
+
+ module("BindingGroup");
+
+ test("has a unique namespace", function() {
+ var bg1, bg2;
+ bg1 = AS.BindingGroup["new"]();
+ bg2 = AS.BindingGroup["new"]();
+ notEqual(bg1.namespace, bg2.namespace);
+ return equal(bg1.namespace[0], "b");
+ });
+
+ test("binds to jquery objects", function() {
+ var bg, object;
+ expect(1);
+ bg = AS.BindingGroup["new"]();
+ object = $("<target>");
+ bg.binds(object, "event", function() {
+ return ok(true);
+ });
+ return object.trigger("event");
+ });
+
+ test("binds to AS.Event event model", function() {
+ var bg, object, _handler;
+ expect(4);
+ bg = AS.BindingGroup["new"]();
+ _handler = function() {
+ return "value";
+ };
+ object = {
+ bind: function(_arg) {
+ var context, event, handler, namespace;
+ event = _arg.event, namespace = _arg.namespace, handler = _arg.handler, context = _arg.context;
+ equal(event, "event");
+ equal(namespace, bg.namespace);
+ equal(_handler(), "value");
+ return equal(context, object);
+ }
+ };
+ return bg.binds(object, "event", _handler, object);
+ });
+
+ test("unbinds bound objects", function() {
+ var bg, handler, object;
+ expect(1);
+ bg = AS.BindingGroup["new"]();
+ object = {
+ bind: function() {},
+ unbind: function(namespace) {
+ return equal(namespace, "." + bg.namespace);
+ }
+ };
+ handler = function() {};
+ bg.binds(object, "event", handler, object);
+ return bg.unbind();
+ });
+
+ test("unbinds bound objects in nested binding groups", function() {
+ var child, handler, object, parent;
+ parent = AS.BindingGroup["new"]();
+ child = parent.addChild();
+ object = {
+ bind: function() {},
+ unbind: function(namespace) {
+ return equal(namespace, "." + child.namespace);
+ }
+ };
+ handler = function() {};
+ child.binds(object, "event", handler, object);
+ return parent.unbind();
+ });
+
+}).call(this);
View
42 test/client/dom.js
@@ -0,0 +1,42 @@
+(function() {
+
+ module("DOM");
+
+ test("creates document fragments", function() {
+ var html;
+ html = AS.DOM["new"]().html(function() {
+ this.head(function() {
+ return this.title("This is the Title");
+ });
+ return this.body(function() {
+ this.h1("This is the Header");
+ this.section(function() {
+ return this.p("I'm the body copy :D");
+ });
+ return this.div({
+ "data-custom": "attributes!"
+ });
+ });
+ });
+ equal($(html).find("title").text(), "This is the Title");
+ equal($(html).find("h1").text(), "This is the Header");
+ equal($(html).find("p").text(), "I'm the body copy :D");
+ return equal($(html).find("[data-custom]").data().custom, "attributes!");
+ });
+
+ test("appends raw (scary html) content", function() {
+ var raw;
+ raw = AS.DOM["new"]().raw("<p>");
+ ok($(raw).find("p").is("p"));
+ return ok($(raw).is("span"));
+ });
+
+ test("appends escaped (non-scary html) content", function() {
+ var raw;
+ raw = AS.DOM["new"]().span(function() {
+ return this.text("<html>");
+ });
+ return equal($(raw).find("html")[0], void 0);
+ });
+
+}).call(this);
View
30 test/client/models/targets.coffee
@@ -1,17 +1,21 @@
-NS.SomeTargets = AS.Models.Targets.extend ({def}) ->
+class NS.SomeTargets < AS.Models.Targets
def selector: "target"
-class ClientRect
- top: 0
- left: 0
- width: 0
- height: 0
+class NS.ClientRect
+ def initialize: (properties) ->
+ @top = 0
+ @left = 0
+ @width = 0
+ @height = 0
+
+ _.extend this, properties
+ console.log "NS.ClientRect", properties, this
- constructor: (properties) ->
- _.extend(this, properties)
@right = @left + @width
@bottom = @top + @height
+ClientRect = NS.ClientRect
+
module "Targets",
setup: ->
body = $("body")
@@ -25,13 +29,13 @@ module "Targets",
@t3 = targets[2]
@t1.getBoundingClientRect = ->
- new ClientRect width: 100, height: 50
+ ClientRect.new width: 100, height: 50
@t2.getBoundingClientRect = ->
- new ClientRect top: 50, width: 100, height: 50
+ ClientRect.new top: 50, width: 100, height: 50
@t3.getBoundingClientRect = ->
- new ClientRect top: 100, width: 100, height: 50
+ ClientRect.new top: 100, width: 100, height: 50
teardown: ->
$("target").remove()
@@ -124,7 +128,7 @@ targetEvent = (x, y) ->
setupEdgeTargets = ->
@targets = AS.Models.Targets.Edge.new()
@el = {}
- @rect = new ClientRect width: 100, height: 50
+ @rect = ClientRect.new width: 100, height: 50
@targets.targets = [
el: @el
rect: @rect
@@ -181,7 +185,7 @@ module "Targets.Thirds",
setup: ->
@targets = AS.Models.Targets.Thirds.new()
@el = {}
- @rect = new ClientRect width: 100, height: 50
+ @rect = ClientRect.new width: 100, height: 50
@targets.targets = [
el: @el
rect: @rect
View
269 test/client/view.js
@@ -0,0 +1,269 @@
+(function() {
+
+ module("View");
+
+ test("generates klass strings", function() {
+ equal(AS.View["new"]().klassString(), "View", "basic klassString is ASView");
+ NS.SomeView = AS.View.extend();
+ return equal(NS.SomeView["new"]().klassString(), "View SomeView", "subclasses include parent class string");
+ });
+
+ test("builds an element", function() {
+ ok(AS.View["new"]().el.is("div"));
+ NS.ListView = AS.View.extend(function(_arg) {
+ var def;
+ def = _arg.def;
+ return def({
+ tagName: "ol"
+ });
+ });
+ return ok(NS.ListView["new"]().el.is("ol.View.ListView"));
+ });
+
+ test("sets options from constructor", function() {
+ return equal(AS.View["new"]({
+ "this": "that"
+ })["this"], "that");
+ });
+
+ test("turns Model options into AS.ViewModels", function() {
+ var it, view;
+ it = AS.Model["new"]();
+ view = AS.View["new"]({
+ it: it
+ });
+ return ok(view.it instanceof AS.ViewModel);
+ });
+
+ test("has a root binding group", function() {
+ return ok(AS.View["new"]().bindingGroup instanceof AS.BindingGroup);
+ });
+
+ test("pluralizes text", function() {
+ var view;
+ view = AS.View["new"]();
+ equal(view.pluralize("cat", 4), "cats");
+ equal(view.pluralize("person", 0), "people");
+ equal(view.pluralize("duck", 1), "duck");
+ equal(view.pluralize("duck", -1), "duck");
+ return equal(view.pluralize("cat", -4), "cats");
+ });
+
+ test("delegates view methods to @el", function() {
+ var el, method, methods, view, _i, _len, _results;
+ methods = ['addClass', 'removeClass', 'show', 'hide', 'html'];
+ expect(methods.length);
+ view = AS.View["new"]();
+ view.el = el = {};
+ _results = [];
+ for (_i = 0, _len = methods.length; _i < _len; _i++) {
+ method = methods[_i];
+ el[method] = function() {
+ return ok(true);
+ };
+ _results.push(view[method]());
+ }
+ return _results;
+ });
+
+ test("allows view element to be set as an option in the constructor", function() {
+ var el, view;
+ el = $("<div>");
+ view = AS.View["new"]({
+ el: el
+ });
+ return equal(el[0], view.currentNode);
+ });
+
+ test("stashes childViews", function() {
+ return deepEqual([], AS.View["new"]().childViews);
+ });
+
+ test("views have a 'view' method to create child views", function() {
+ var returned, subEl, view;
+ view = AS.View["new"]();
+ returned = view.view(AS.View, {
+ el: subEl = view.div()
+ });
+ equal(subEl, returned);
+ equal(subEl, view.childViews[0].el[0]);
+ return equal(view.childViews[0], view.bindingGroup.children.reverse()[0]);
+ });
+
+ module("View.binding()");
+
+ test("creates a binding for a collection", function() {
+ var collection, view;
+ view = AS.View["new"]();
+ collection = AS.Collection["new"]();
+ return ok(view.binding(collection, function() {}).constructor === AS.Binding.Many);
+ });
+
+ test("creates a binding for a model", function() {
+ var model, view;
+ view = AS.View["new"]();
+ model = AS.Model["new"]();
+ return ok(view.binding(model).constructor === AS.Binding.Model);
+ });
+
+ module("View.descendantViews()");
+
+ test("returns all descendantViews", function() {
+ var view;
+ view = AS.View["new"]();
+ view.view(AS.View);
+ view.childViews[0].view(AS.View);
+ return equal(2, view.descendantViews().length);
+ });
+
+ test("filters descendantViews by constructor", function() {
+ var view;
+ NS.SubView = AS.View.extend();
+ view = AS.View["new"]();
+ view.view(NS.SubView);
+ view.childViews[0].view(AS.View);
+ return equal(1, view.descendantViews(null, NS.SubView).length);
+ });
+
+ module("View Integration: ");
+
+ test("property binding with two children cleans up all content when property changes", function() {
+ NS.Parent = AS.Model.extend(function(_arg) {
+ var def, defs, delegate, include;
+ delegate = _arg.delegate, include = _arg.include, def = _arg.def, defs = _arg.defs;
+ return this.property("item");
+ });
+ NS.Item = AS.Model.extend(function(_arg) {
+ var def, defs, delegate, include;
+ delegate = _arg.delegate, include = _arg.include, def = _arg.def, defs = _arg.defs;
+ return this.hasMany('children', {
+ model: function() {
+ return NS.Child;
+ }
+ });
+ });
+ NS.Child = AS.Model.extend(function(_arg) {
+ var def, defs, delegate, include;
+ delegate = _arg.delegate, include = _arg.include, def = _arg.def, defs = _arg.defs;
+ return this.field('name');
+ });
+ this.item1 = NS.Item["new"]();
+ this.item2 = NS.Item["new"]();
+ this.item1.children.add({
+ name: "child1"
+ });
+ this.item1.children.add({
+ name: "child2"
+ });
+ this.item2.children.add({
+ name: "child3"
+ });
+ this.item2.children.add({
+ name: "child4"
+ });
+ this.parent = NS.Parent["new"]({
+ item: this.item1
+ });
+ NS.View = AS.View.extend(function(_arg) {
+ var def, defs, delegate, include;
+ delegate = _arg.delegate, include = _arg.include, def = _arg.def, defs = _arg.defs;
+ return def({
+ content: function() {
+ var binding;
+ return binding = this.parent.binding('item', function(item) {
+ this.ol(function() {
+ return item.binding('children', function(child) {
+ return this.li(function() {
+ return child.binding('name');
+ });
+ });
+ });
+ return this.ul(function() {
+ return item.binding('children', function(child) {
+ return this.li(function() {
+ return child.binding('name');
+ });
+ });
+ });
+ });
+ }
+ });
+ });
+ this.view = NS.View["new"]({
+ parent: this.parent
+ });
+ equal(2, this.view.el.find("ol li").length, "ordered list items are inserted");
+ equal(2, this.view.el.find("ul li").length, "unordered list items are inserted");
+ this.parent.item.set(null);
+ Taxi.Governer.exit();
+ equal(0, this.view.el.find("ol li").length, "ordered list items are removed");
+ return equal(0, this.view.el.find("ul li").length, "unordered list items are removed");
+ });
+
+ test("property binding nested ina property binding test cleans up bindings", function() {
+ var otherBinding;
+ NS.Model = AS.Model.extend(function(_arg) {
+ var def, defs, delegate, include;
+ delegate = _arg.delegate, include = _arg.include, def = _arg.def, defs = _arg.defs;
+ this.property("other");
+ return this.hasMany("children");
+ });
+ NS.View = AS.View.extend(function(_arg) {
+ var def, defs, delegate, include;
+ delegate = _arg.delegate, include = _arg.include, def = _arg.def, defs = _arg.defs;
+ return def({
+ content: function() {
+ return this.root.binding('other', function(other) {
+ var binding;
+ this.section({
+ "class": "root-other",
+ id: other.model.objectId()
+ }, function() {
+ return other.binding('children', function(child) {
+ return this.section({
+ id: child.model.objectId()
+ });
+ });
+ });
+ return binding = this.model.binding('other', function() {
+ return this.section({
+ "class": "model-other"
+ });
+ });
+ });
+ }
+ });
+ });
+ this.root = NS.Model["new"]();
+ this.thing1 = NS.Model["new"]({
+ children: [{}]
+ });
+ this.thing2 = NS.Model["new"]({
+ children: [{}, {}]
+ });
+ this.other1 = NS.Model["new"]();
+ this.other2 = NS.Model["new"]();
+ this.model = NS.Model["new"]({
+ other: this.other1
+ });
+ this.view = NS.View["new"]({
+ root: this.root,
+ model: this.model
+ });
+ otherBinding = NS.OtherBinding;
+ this.root.other.set(this.thing1);
+ Taxi.Governer.exit();
+ ok(this.view.el.find("#" + (this.thing1.objectId())).is("*"), "renders first thing");
+ ok(this.view.el.find("#" + (this.thing1.children.at(0).objectId())).is("*"), "renders first thing child");
+ this.root.other.set(this.thing2);
+ Taxi.Governer.exit();
+ ok(this.view.el.find("#" + (this.thing2.objectId())).is("*"), "renders second thing");
+ ok(this.view.el.find("#" + (this.thing2.children.at(0).objectId())).is("*"), "renders second thing first child");
+ ok(this.view.el.find("#" + (this.thing2.children.at(1).objectId())).is("*"), "renders second thing second child");
+ this.model.other.set(this.other2);
+ Taxi.Governer.exit();
+ equal(1, this.view.el.find(".model-other").length, "renders only one other");
+ return ok(this.view.el.find("#" + (this.thing2.objectId())).is("*"), "second thing still visiible");
+ });
+
+}).call(this);
View
57 test/client/view_events.js
@@ -0,0 +1,57 @@
+(function() {
+
+ module("ViewEvents");
+
+ test("delegates events", function() {
+ var BoundView, view;
+ expect(3);
+ BoundView = AS.View.extend(function(_arg) {
+ var def;
+ def = _arg.def;
+ def({
+ events: {
+ "click": "click_handler",
+ "click button": "button_handler",
+ "event @member": "member_handler"
+ }
+ });
+ def({
+ initialize: function() {
+ this.member = AS.Model["new"]();
+ this._super.apply(this, arguments);
+ return this._button = this.$(this.button());
+ }
+ });
+ def({
+ click_handler: function() {
+ return ok(true);
+ }
+ });
+ def({
+ member_handler: function() {
+ return ok(true);
+ }
+ });
+ def({
+ button_handler: function() {
+ return ok(true);
+ }
+ });
+ def({
+ guard_fail_handler: function() {
+ return ok(true);
+ }
+ });
+ return def({
+ guard_pass_handler: function() {
+ return ok(true);
+ }
+ });
+ });
+ view = BoundView["new"]();
+ view.member.trigger("event");
+ Taxi.Governer.exit();
+ return view._button.trigger("click");
+ });
+
+}).call(this);
View
4 test/client/view_model.coffee
@@ -1,5 +1,5 @@
-NS.AView = AS.View.extend()
-NS.Viewed = AS.Model.extend ({def}) ->
+class NS.AView < AS.View
+class NS.Viewed < AS.Model
@field "field"
@hasMany "many"
@hasOne "one"
View
43 test/client/view_model.js
@@ -0,0 +1,43 @@
+(function() {
+
+ NS.AView = AS.View.extend();
+
+ NS.Viewed = AS.Model.extend(function(_arg) {
+ var def;
+ def = _arg.def;
+ this.field("field");
+ this.hasMany("many");
+ this.hasOne("one");
+ return def({
+ other: function() {}
+ });
+ });
+
+ module("ViewModel");
+
+ test("builds viewmodels", function() {
+ var model, view, vm;
+ view = NS.AView["new"]();
+ model = NS.Viewed["new"]();
+ vm = AS.ViewModel.build(view, model);
+ equal(vm.view, view);
+ return equal(vm.model, model);
+ });
+
+ test("caches constructors", function() {
+ var vm1, vm2;
+ vm1 = AS.ViewModel.build(NS.AView["new"](), NS.Viewed["new"]());
+ vm2 = AS.ViewModel.build(NS.AView["new"](), NS.Viewed["new"]());
+ return equal(vm1.constructor, vm2.constructor);
+ });
+
+ test("configures constructor", function() {
+ var bindables, vm;
+ vm = AS.ViewModel.build(NS.AView["new"](), NS.Viewed["new"]());
+ bindables = vm.constructor.bindables;
+ equal(bindables.field, AS.Binding.Field);
+ equal(bindables.many, AS.Binding.Many);
+ return equal(bindables.one, AS.Binding.One);
+ });
+
+}).call(this);
View
5 test/client_helper.js
@@ -0,0 +1,5 @@
+(function() {
+
+ AS.require("client");
+
+}).call(this);
View
4 test/core.coffee
@@ -3,8 +3,8 @@ test "testIdentity", ->
ok AS.Identity(10)(10)
test "constructorIdentity", ->
- class Fake
- ok AS.ConstructorIdentity(Fake)(new Fake)
+ class NS.Fake
+ ok AS.ConstructorIdentity(NS.Fake)(NS.Fake.new())
test "deepClone", ->
notEqual AS.deepClone(it = []), it
View
57 test/core.js
@@ -0,0 +1,57 @@
+(function() {
+
+ module("utilities");
+
+ test("testIdentity", function() {
+ return ok(AS.Identity(10)(10));
+ });
+
+ test("constructorIdentity", function() {
+ var Fake;
+ Fake = (function() {
+
+ function Fake() {}
+
+ return Fake;
+
+ })();
+ return ok(AS.ConstructorIdentity(Fake)(new Fake));
+ });
+
+ test("deepClone", function() {
+ var it, not_it;
+ notEqual(AS.deepClone(it = []), it);
+ notEqual(AS.deepClone(it = {}), it);
+ deepEqual(AS.deepClone(it = []), it);
+ deepEqual(AS.deepClone(it = {}), it);
+ it = [
+ {
+ a: 134,
+ 3: [2, {}, [], [], "FOO"]
+ }, 23, "BAR"
+ ];
+ deepEqual(AS.deepClone(it), it);
+ not_it = AS.deepClone(it);
+ not_it.push("BAZ");
+ return notDeepEqual(it, not_it);
+ });
+
+ test("uniq", function() {
+ ok(AS.uniq().match(/^\w+$/));
+ return notEqual(AS.uniq(), AS.uniq());
+ });
+
+ test("humanSize", function() {
+ var index, prefix, sz, _len, _ref, _results;
+ sz = AS.humanSize;
+ equal(sz(100), "100.0 B");
+ _ref = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
+ _results = [];
+ for (index = 0, _len = _ref.length; index < _len; index++) {
+ prefix = _ref[index];
+ _results.push(equal(sz(Math.pow(1024, index + 1)), "1.0 " + prefix));
+ }
+ return _results;
+ });
+
+}).call(this);
View
36 test/core/callbacks.js
@@ -0,0 +1,36 @@
+(function() {
+ var WithCallbacks;
+
+ WithCallbacks = AS.Object.extend(function(_arg) {
+ var include;
+ include = _arg.include;
+ include(AS.Callbacks);
+ return this.defineCallbacks({
+ before: "this that".split(" ")
+ });
+ });
+
+ module("Callbacks");
+
+ test("definition", function() {
+ var it;
+ it = WithCallbacks;
+ ok(WithCallbacks.beforeThis);
+ return ok(WithCallbacks.beforeThat);
+ });
+
+ test("running", function() {
+ var cb, it, one;
+ expect(2);
+ it = WithCallbacks;
+ cb = function() {
+ return ok(true);
+ };
+ it.beforeThis(cb);
+ it.beforeThat(cb);
+ one = WithCallbacks["new"]();
+ one.runCallbacks("beforeThis");
+ return one.runCallbacks("beforeThat");
+ });
+
+}).call(this);
View
149 test/core/collection.js
@@ -0,0 +1,149 @@
+(function() {
+ var C;
+
+ C = AS.Namespace["new"]("Collections");
+
+ module("Collection");
+
+ test("sets the inverse is specified", function() {
+ var things;
+ C.Thing = AS.Model.extend();
+ C.Thing.property("inverse");
+ C.Thing.property("name");
+ C.ThingCollection = AS.Collection.extend(function() {
+ this.def({
+ model: function() {
+ return C.Thing;
+ }
+ });
+ return this.def({
+ inverse: "inverse"
+ });
+ });
+ things = C.ThingCollection["new"]();
+ things.source = "SOURCE";
+ things.add();
+ return equal("SOURCE", things.first().value().inverse.get());
+ });
+
+ test("clears inverse if specified", function() {
+ var thing, things;
+ C.Thing = AS.Model.extend();
+ C.Thing.property("inverse");
+ C.ThingCollection = AS.Collection.extend(function() {
+ this.def({
+ model: function() {
+ return C.Thing;
+ }
+ });
+ return this.def({
+ inverse: "inverse"
+ });
+ });
+ things = C.ThingCollection["new"]();
+ things.source = "SOURCE";
+ thing = things.add();
+ things.remove(thing);
+ return equal(null, thing.inverse.get());
+ });
+
+ test("inserts item of specified type", function() {
+ var things;
+ C.Thing = AS.Model.extend();
+ C.ThingCollection = AS.Collection.extend(function() {
+ return this.def({
+ model: function() {
+ return C.Thing;
+ }
+ });
+ });
+ things = C.ThingCollection["new"]();
+ things.add();
+ return ok(things.first().value() instanceof C.Thing);
+ });
+
+ test("inserts item at a specified index", function() {
+ var thing, things;
+ things = AS.Collection["new"]();
+ things.add();
+ things.add();
+ thing = things.add({}, {
+ at: 1
+ });
+ equal(things.length, 3);
+ return equal(things.at(1), thing);
+ });
+
+ test("remove item from collection", function() {
+ var thing, things;
+ things = AS.Collection["new"]();
+ thing = things.add();
+ things.remove(thing);
+ return equal(things.length, 0);
+ });
+
+ module("Events");
+
+ test("add event", function() {
+ var collection;
+ expect(1);
+ collection = AS.Collection["new"]();
+ collection.bind("add", function() {
+ return ok(true);
+ });
+ collection.add();
+ return Taxi.Governer.exit();
+ });
+
+ test("remove event", function() {
+ var collection, thing;
+ expect(1);
+ collection = AS.Collection["new"]();
+ thing = collection.add();
+ collection.bind("remove", function() {
+ return ok(true);
+ });
+ collection.remove(thing);
+ return Taxi.Governer.exit();
+ });
+
+ test("model change events bubble through collection", function() {
+ var collection, thing;
+ expect(1);
+ C.Thing = AS.Model.extend();
+ C.Thing.property("name");
+ collection = AS.Collection["new"]();
+ thing = collection.add(C.Thing["new"]());
+ collection.bind("change", function() {
+ return ok(true);
+ });
+ thing.name.set("changed");
+ return Taxi.Governer.exit();
+ });
+
+ test("add events capture on collection", function() {
+ var collection, thing;
+ expect(1);
+ thing = AS.Model["new"]();
+ collection = AS.Collection["new"]();
+ thing.bind("add", function() {
+ return ok(true);
+ });
+ collection.add(thing);
+ return Taxi.Governer.exit();
+ });
+
+ test("remove events capture on collection", function() {
+ var collection, thing;
+ expect(1);
+ thing = AS.Model["new"]();
+ collection = AS.Collection["new"]();
+ thing.bind("remove", function() {
+ return ok(true);
+ });
+ collection.add(thing);
+ collection.remove(thing);
+ return Taxi.Governer.exit();
+ });
+
+}).call(this);
View
136 test/core/filtered_collection.js
@@ -0,0 +1,136 @@
+(function() {
+ var C;
+
+ C = AS.Namespace["new"]("Collections");
+
+ C.Model = AS.Model.extend(function(_arg) {
+ var def, defs, delegate, include;
+ delegate = _arg.delegate, include = _arg.include, def = _arg.def, defs = _arg.defs;
+ return this.field("truth", {
+ type: AS.Model.Boolean,
+ "default": false
+ });
+ });
+
+ C.Collection = AS.Collection.extend(function(_arg) {
+ var def, defs, delegate, include;
+ delegate = _arg.delegate, include = _arg.include, def = _arg.def, defs = _arg.defs;
+ });
+
+ module("FilteredCollection");
+
+ test("by default, all members are in the filtered collection", function() {
+ var c, f, one, three, two;
+ c = C.Collection["new"]();
+ f = c.filter();
+ one = c.add(C.Model["new"]());
+ two = c.add(C.Model["new"]());
+ three = c.add(C.Model["new"]());
+ Taxi.Governer.exit();
+ return deepEqual(f.models.value(), [one, two, three]);
+ });
+
+ test("removes items from filter when they are removed from collection", function() {
+ var c, f, one, three, two;
+ c = C.Collection["new"]();
+ f = c.filter();
+ one = c.add(C.Model["new"]());
+ two = c.add(C.Model["new"]());
+ three = c.add(C.Model["new"]());
+ c.remove(two);
+ Taxi.Governer.exit();
+ return deepEqual(f.models.value(), [one, three]);
+ });
+
+ test("respects filter function when adding models", function() {
+ var c, f, one, three, two;
+ c = C.Collection["new"]();
+ f = c.filter({
+ truth: false
+ });
+ one = c.add(C.Model["new"]());
+ two = c.add(C.Model["new"]({
+ truth: true
+ }));
+ three = c.add(C.Model["new"]());
+ Taxi.Governer.exit();
+ return deepEqual(f.models.value(), [one, three]);
+ });
+
+ test("add filtered items when they change", function() {
+ var c, f, one, three, two;
+ c = C.Collection["new"]();
+ f = c.filter({
+ truth: false
+ });
+ one = c.add(C.Model["new"]());
+ two = c.add(C.Model["new"]({
+ truth: true
+ }));
+ three = c.add(C.Model["new"]());
+ two.truth.set(false);
+ Taxi.Governer.exit();
+ return deepEqual(f.models.pluck('id').sort().value(), _([one, three, two]).pluck("id").sort());
+ });
+
+ test("remove filtered items when they change", function() {
+ var c, f, one, three, two;
+ c = C.Collection["new"]();
+ f = c.filter({
+ truth: false
+ });
+ one = c.add(C.Model["new"]());
+ two = c.add(C.Model["new"]());
+ three = c.add(C.Model["new"]());
+ two.truth.set(true);
+ Taxi.Governer.exit();
+ return deepEqual(f.models.value(), [one, three]);
+ });
+
+ test("triggers add/remove events", function() {
+ var c, f, one, three, two;
+ expect(5);
+ c = C.Collection["new"]();
+ f = c.filter({
+ truth: false
+ });
+ f.bind("add", function() {
+ return ok(true);
+ });
+ f.bind("remove", function() {
+ return ok(true);
+ });
+ one = c.add(C.Model["new"]());
+ Taxi.Governer.exit();
+ two = c.add(C.Model["new"]({
+ truth: true
+ }));
+ Taxi.Governer.exit();
+ three = c.add(C.Model["new"]());
+ Taxi.Governer.exit();
+ two.truth.set(false);
+ Taxi.Governer.exit();
+ two.truth.set(true);
+ Taxi.Governer.exit();
+ return deepEqual(f.models.pluck('id').value(), [one.id, three.id]);
+ });
+
+ test("re-filters when filter changes", function() {
+ var c, f, one, three, two;
+ c = C.Collection["new"]();
+ f = c.filter({
+ truth: false
+ });
+ one = c.add(C.Model["new"]());
+ two = c.add(C.Model["new"]({
+ truth: true
+ }));
+ three = c.add(C.Model["new"]());
+ f.setConditions({
+ truth: true
+ });
+ Taxi.Governer.exit();
+ return deepEqual(f.models.value(), [two]);
+ });
+
+}).call(this);
View
42 test/core/instance_methods.js