lanes 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/client/lanes/access/LoginDialog.coffee +0 -1
  3. data/client/lanes/access/screens/user-management/UserManagement.coffee +0 -1
  4. data/client/lanes/components/grid/Grid.coffee +7 -2
  5. data/client/lanes/components/radio-group/RadioGroup.coffee +0 -1
  6. data/client/lanes/components/record-finder/Clause.coffee +0 -1
  7. data/client/lanes/components/record-finder/Dialog.coffee +2 -2
  8. data/client/lanes/components/record-finder/RecordFinder.coffee +3 -2
  9. data/client/lanes/components/select2/Select2.coffee +8 -0
  10. data/client/lanes/components/select2/index.js +2 -0
  11. data/client/lanes/extension/Extensions.coffee +1 -1
  12. data/client/lanes/lib/ModuleSupport.coffee +1 -1
  13. data/client/lanes/models/ChangeSet.coffee +0 -1
  14. data/client/lanes/models/ModelAssociations.coffee +10 -10
  15. data/client/lanes/models/mixins/HasCodeField.coffee +3 -3
  16. data/client/lanes/screens/Definitions.coffee +0 -6
  17. data/client/lanes/views/ModelUpdate.coffee +0 -1
  18. data/client/lanes/workspace/ActiveScreensSwitcher.coffee +0 -1
  19. data/client/lanes/workspace/Layout.coffee +0 -1
  20. data/client/lanes/workspace/Navbar.coffee +0 -1
  21. data/client/lanes/workspace/Pages.coffee +0 -1
  22. data/client/lanes/workspace/ScreensMenu.coffee +0 -3
  23. data/lib/lanes/access/authentication_provider.rb +7 -7
  24. data/lib/lanes/api/coffeescript_processor.rb +64 -0
  25. data/lib/lanes/api/javascript_processor.rb +49 -41
  26. data/lib/lanes/api/root.rb +3 -1
  27. data/lib/lanes/concerns/pub_sub.rb +38 -40
  28. data/lib/lanes/configuration.rb +15 -9
  29. data/lib/lanes/version.rb +1 -1
  30. data/spec/command-reference-files/initial/Gemfile +1 -1
  31. data/spec/command-reference-files/initial/client/appy-app/Extension.coffee +0 -1
  32. data/spec/command-reference-files/model/client/appy-app/models/TestTest.coffee +0 -1
  33. data/spec/command-reference-files/screen/client/appy-app/screens/ready-set-go/ReadySetGo.coffee +0 -1
  34. data/spec/command-reference-files/screen/config/screens.rb +1 -0
  35. data/spec/command-reference-files/view/client/appy-app/views/BigView.coffee +0 -1
  36. data/spec/{api/javascript_processor_spec.rb → server/api/coffeescript_processor_spec.rb} +8 -2
  37. data/spec/server/concerns/pub_sub_spec.rb +55 -56
  38. data/templates/client/Extension.coffee +0 -1
  39. data/templates/client/models/Model.coffee +0 -1
  40. data/templates/client/screens/Screen.coffee +0 -1
  41. data/templates/client/views/View.coffee +0 -1
  42. data/templates/config/screen.rb +1 -0
  43. metadata +6 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 09c525f4869a2e1f8b1d4ff92ca1f31344715994
4
- data.tar.gz: 98893dd650c0d95209e9b337623f1c079e807ae6
3
+ metadata.gz: b111e3e03f3c6f6a3bfffca0d087590b1ea0277a
4
+ data.tar.gz: ac11d9b1700311f927f06d06bcbee08de31c56e2
5
5
  SHA512:
6
- metadata.gz: e8a11e0a3028195b5cf5bf3ff6f1e2cc06754e08e776100fcc69abf330cf1cadcf1c8e8b72d408b60f73266bd719feebc0d523b4415a847957cc7f9d947ef732
7
- data.tar.gz: a813d60b1ad735443aa90e5461743836f0eb606c81464e61e6d1131dc9e911fb5faba4a345ca876a474c3104205f360f19f1edaf0bb376bcbbe42b1f3fd56d91
6
+ metadata.gz: 3fd3233d6311c8df5041a5a39e2245f5e5099fbddde5126c8b4202e51bb79e51a25391bec8a18f9c51d13d3d34f66e7e22e5ce54a0fc88e42ea9e568bfe473ce
7
+ data.tar.gz: bd91444bafa49fc750fb6750071903a4856bff5ececf54c577935b55382788798f5b574f57b91fd41f56fb80bb5dece54b208fbc981e8b23f69d3519e0ce7d48
@@ -8,7 +8,6 @@ class Lanes.Access.LoginDialog extends Lanes.Components.ModalDialog
8
8
 
9
9
  size: 'md'
10
10
  title: 'Please sign in …'
11
- FILE: FILE
12
11
 
13
12
  domEvents:
14
13
  'click .btn-primary': 'onLogin'
@@ -2,7 +2,6 @@ class Lanes.Access.Screens.UserManagement extends Lanes.Screens.Base
2
2
 
3
3
  templateName: 'user-management/screen'
4
4
  templatePrefix: 'lanes/access/screens'
5
- FILE: FILE
6
5
 
7
6
  subviews:
8
7
  grid:
@@ -8,7 +8,6 @@ _.extend( Lanes.$.fn.dataTableExt.oStdClasses,{
8
8
 
9
9
 
10
10
  class Lanes.Components.Grid extends Lanes.Components.Base
11
- FILE: FILE
12
11
 
13
12
  domEvents:
14
13
  'click button.refresh': 'reload'
@@ -48,6 +47,7 @@ class Lanes.Components.Grid extends Lanes.Components.Base
48
47
  selectionMode: ['string', true, 'single']
49
48
  editorConfig: { type: 'object' }
50
49
  editingController: 'any' # either string or constructor fn
50
+ columnWidths: { type: 'array', default: -> [] }
51
51
 
52
52
  initialize: ->
53
53
  if this.viewport
@@ -191,7 +191,12 @@ class Lanes.Components.Grid extends Lanes.Components.Base
191
191
  align = switch query_field.type
192
192
  when 'integer','bigdec' then 'r'
193
193
  else 'l'
194
- { title: query_field.title, id: query_field.id, className: align, "targets": [ index ] }
194
+ options = {
195
+ id: query_field.id, title: query_field.title,
196
+ className: align, "targets": [ index ]
197
+ }
198
+ options['width'] = @columnWidths[index] if @columnWidths[index]
199
+ options
195
200
 
196
201
  _dataFromModel: (model)->
197
202
  data = []
@@ -17,7 +17,6 @@ class RadioInput extends Lanes.Views.Base
17
17
 
18
18
  class Lanes.Components.RadioGroup extends Lanes.Components.MultiSelect
19
19
 
20
- FILE: FILE
21
20
 
22
21
  writeTemplate: ->
23
22
  "<span data-hook='choices-input'></span>"
@@ -1,5 +1,4 @@
1
1
  class Lanes.Components.RecordFinder.Clause extends Lanes.Components.Base
2
- FILE: FILE
3
2
 
4
3
  writeTemplateName: 'clause'
5
4
  useFormBindings: true
@@ -1,5 +1,4 @@
1
1
  class Lanes.Components.RecordFinder.Dialog extends Lanes.Components.ModalDialog
2
- FILE: FILE
3
2
 
4
3
  domEvents:
5
4
  'click .add-clause': 'addClause'
@@ -12,7 +11,7 @@ class Lanes.Components.RecordFinder.Dialog extends Lanes.Components.ModalDialog
12
11
  hook: 'grid'
13
12
  component: 'Grid'
14
13
  options: ->
15
- { recordQuery: @recordQuery }
14
+ _.extend(@gridOptions||{}, { recordQuery: @recordQuery, options: @gridOptions })
16
15
  query_clauses:
17
16
  container: '.query-clauses'
18
17
  view: Lanes.Components.RecordFinder.Clause, collection: 'clauses'
@@ -22,6 +21,7 @@ class Lanes.Components.RecordFinder.Dialog extends Lanes.Components.ModalDialog
22
21
 
23
22
  session:
24
23
  recordQuery: 'model'
24
+ gridOptions: 'object'
25
25
  clauses: 'collection'
26
26
 
27
27
  initialize:(options)->
@@ -1,5 +1,4 @@
1
1
  class Lanes.Components.RecordFinder extends Lanes.Components.Base
2
- FILE: FILE
3
2
 
4
3
  writeTemplateName: 'field'
5
4
  writeTemplateData: ->
@@ -13,6 +12,7 @@ class Lanes.Components.RecordFinder extends Lanes.Components.Base
13
12
  withAssociations: 'array'
14
13
  invalid_chars: 'regex'
15
14
  recordQuery: 'model'
15
+ gridOptions: 'gridOptions'
16
16
  fieldName: 'string'
17
17
  queryField: [ 'string', false, 'code' ]
18
18
 
@@ -30,7 +30,8 @@ class Lanes.Components.RecordFinder extends Lanes.Components.Base
30
30
 
31
31
  displayFinder: ->
32
32
  finder = new Lanes.Components.RecordFinder.Dialog(
33
- parent:this, title: @title, recordQuery: @recordQuery
33
+ gridOptions: @gridOptions, parent:this
34
+ title: @title, recordQuery: @recordQuery
34
35
  )
35
36
  finder.show().then( (dlg)->
36
37
  dlg.remove().record
@@ -0,0 +1,8 @@
1
+ class Lanes.Components.Select2 extends Lanes.Components.Base
2
+
3
+ writeTemplate: ->
4
+ multiple = if this.multiple then "multiple" else ""
5
+ "<div><select class='form-control' #{multiple} name='#{this.field_name}' data-hook='choices-input'></select></div>"
6
+
7
+ readTemplate: ->
8
+ "<div class='ro-input' name='#{this.field_name}'></div>"
@@ -0,0 +1,2 @@
1
+ //= require ./template
2
+ //= require ./Select2
@@ -18,7 +18,7 @@ Lanes.Extensions = {
18
18
 
19
19
  makeNamespace: (identifier)->
20
20
  for ns in ['Models','Views','Controllers','Screens','Components']
21
- Lanes.namespace("#{identifier}.#{ns}")
21
+ Lanes.namespace("#{identifier}.#{ns}.Mixins")
22
22
 
23
23
  controlling: ->
24
24
  this.get( @controlling_id )
@@ -18,5 +18,5 @@ class Lanes.lib.ModuleSupport
18
18
  _.extend(@::[key], value)
19
19
  else
20
20
  @::[key] = value
21
- obj.included?.apply(@)
21
+ obj.included?(@)
22
22
  this
@@ -1,6 +1,5 @@
1
1
  class Lanes.Models.ChangeSet extends Lanes.Models.Base
2
2
 
3
- FILE: FILE
4
3
 
5
4
  constructor: ->
6
5
  super
@@ -15,7 +15,7 @@ class Lanes.Models.AssocationMap
15
15
  getClassFor: (name)->
16
16
  definition = @definitions[name]
17
17
  object = definition.model || definition.collection
18
- Lanes.u.findObject( object, 'Models', @klass::FILE)
18
+ Lanes.u.findObject(object, 'Models', @klass::FILE)
19
19
 
20
20
  getOptions: (name, definition, model)->
21
21
  options = { parent: model }
@@ -24,32 +24,32 @@ class Lanes.Models.AssocationMap
24
24
  options
25
25
 
26
26
  # will be called in the scope of the model
27
- createModel: (association, name, definition, fk, pk, target_klass)->
28
- target_klass ||= association.getClassFor(name)
27
+ createModel: (association, name, definition, fk, pk, target_class)->
28
+ target_class ||= association.getClassFor(name)
29
29
  options = association.getOptions(name,definition,this)
30
30
  model_id = this.get(pk)
31
31
  if model_id && model_id == this._cache[name]?.id
32
32
  this._cache[name]
33
33
  else
34
- target_klass.findOrCreate(options)
34
+ target_class.findOrCreate(options)
35
35
 
36
36
  # will be called in the scope of the model
37
- createCollection: (association, name, definition, fk, pk, target_klass)->
38
- target_klass ||= association.getClassFor(name)
37
+ createCollection: (association, name, definition, fk, pk, target_class)->
38
+ target_class ||= association.getClassFor(name)
39
39
  options = association.getOptions(name,definition,this)
40
40
  options.filter ||= {}
41
41
  options.filter[fk]=this.get(pk)
42
42
 
43
- if target_klass::isCollection
44
- new target_klass(options.models||[],options)
43
+ if target_class::isCollection
44
+ new target_class(options.models||[],options)
45
45
  else
46
- options.model=target_klass
46
+ options.model=target_class
47
47
  new Lanes.Models.AssociationCollection(options.models||[],options)
48
48
  # returns the definition for the derived property
49
49
  derivedDefinition: (name,definition)->
50
50
  createFn = _.partial(
51
51
  if definition.model then this.createModel else this.createCollection,
52
- this, name, definition, this.fk(name), this.pk(name), this.getClassFor(name)
52
+ this, name, definition, this.fk(name), this.pk(name)
53
53
  )
54
54
  { deps: [this.pk(name)], fn: createFn }
55
55
 
@@ -2,11 +2,11 @@ Lanes.Models.Mixins.HasCodeField = {
2
2
 
3
3
  INVALID: /[^A-Z0-9a-z]/
4
4
 
5
- included: ->
6
- this.prototype.INVALID_CODE_CLanes.RS ||= Lanes.Models.mixins.Lanes.sCodeField.INVALID
5
+ included: (klass)->
6
+ klass::INVALID_CODE_CHARS ||= Lanes.Models.mixins.Lanes.sCodeField.INVALID
7
7
 
8
8
  initialize: ->
9
- this.on('change:code', this.upcaseCode )
9
+ this.on('change:code', this.upcaseCode)
10
10
 
11
11
  upcaseCode: ->
12
12
  this.set('code', this.get('code').toUpperCase())
@@ -1,5 +1,4 @@
1
1
  class ScreenView extends Lanes.Models.BasicModel
2
- FILE: FILE
3
2
 
4
3
  session:
5
4
  screen: 'object'
@@ -31,7 +30,6 @@ class ScreenView extends Lanes.Models.BasicModel
31
30
 
32
31
 
33
32
  class ScreenDefinition extends Lanes.Models.BasicModel
34
- FILE: FILE
35
33
 
36
34
  session:
37
35
  id: 'string'
@@ -86,7 +84,6 @@ class ScreenDefinition extends Lanes.Models.BasicModel
86
84
 
87
85
 
88
86
  class ScreenViewSet extends Lanes.Models.BasicCollection
89
- FILE: FILE
90
87
 
91
88
  model: ScreenView
92
89
 
@@ -125,7 +122,6 @@ class ScreenViewSet extends Lanes.Models.BasicCollection
125
122
 
126
123
 
127
124
  class ScreenSet extends Lanes.Models.BasicCollection
128
- FILE: FILE
129
125
 
130
126
  model: ScreenDefinition
131
127
 
@@ -138,7 +134,6 @@ class ScreenSet extends Lanes.Models.BasicCollection
138
134
 
139
135
 
140
136
  class MenuGroup extends Lanes.Models.BasicModel
141
- FILE: FILE
142
137
 
143
138
  session:
144
139
  id: 'string'
@@ -156,7 +151,6 @@ class MenuGroup extends Lanes.Models.BasicModel
156
151
  })
157
152
 
158
153
  class MenuGroupSet extends Lanes.Models.BasicCollection
159
- FILE: FILE
160
154
 
161
155
  constructor: -> super
162
156
  model: MenuGroup
@@ -1,6 +1,5 @@
1
1
  class Lanes.Views.ModelUpdate extends Lanes.Views.Base
2
2
 
3
- FILE: FILE
4
3
 
5
4
  templateData: ->
6
5
  { change: @model }
@@ -1,7 +1,6 @@
1
1
  class TabView extends Lanes.Views.Base
2
2
 
3
3
  template: "<li><a data-toggle='tab'></a><span class='close'>×</span></li>"
4
- FILE: FILE
5
4
  mixins:[
6
5
  Lanes.Workspace.WorkspaceView
7
6
  ]
@@ -1,6 +1,5 @@
1
1
  class Lanes.Workspace.Layout extends Lanes.Views.Base
2
2
 
3
- FILE: FILE
4
3
  templateName: 'layout'
5
4
  templateData: ->
6
5
  { ui: this.ui_state }
@@ -1,7 +1,6 @@
1
1
  class Lanes.Workspace.Navbar extends Lanes.Views.Base
2
2
 
3
3
  templateName: 'navbar'
4
- FILE: FILE
5
4
  mixins:[
6
5
  Lanes.Workspace.WorkspaceView
7
6
  ]
@@ -1,7 +1,6 @@
1
1
  class Lanes.Workspace.Pages extends Lanes.Views.Base
2
2
 
3
3
  templateName: 'pages'
4
- FILE: FILE
5
4
  mixins:[
6
5
  Lanes.Workspace.WorkspaceView
7
6
  ]
@@ -1,7 +1,6 @@
1
1
  class ScreenList extends Lanes.Views.Base
2
2
 
3
3
  template: "<li><a href='#'><span></span><i></i></a></li>"
4
- FILE: FILE
5
4
  mixins:[
6
5
  Lanes.Workspace.WorkspaceView
7
6
  ]
@@ -19,7 +18,6 @@ class ScreenList extends Lanes.Views.Base
19
18
  class ScreenGroup extends Lanes.Views.Base
20
19
 
21
20
  template: '<li class="group"><a class="heading" href="#"><span></span><i></i></a><ul></ul></li>'
22
- FILE: FILE
23
21
  mixins:[
24
22
  Lanes.Workspace.WorkspaceView
25
23
  ]
@@ -47,7 +45,6 @@ class ScreenGroup extends Lanes.Views.Base
47
45
 
48
46
  class Lanes.Workspace.ScreensMenu extends Lanes.Views.Base
49
47
 
50
- FILE: FILE
51
48
  templateName: 'screens-menu'
52
49
  mixins:[
53
50
  Lanes.Workspace.WorkspaceView
@@ -10,10 +10,10 @@ module Lanes
10
10
 
11
11
  def current_user
12
12
  @current_user ||= (
13
- if Lanes.env.test? && request.env['HTTP_X_TESTING_USER']
13
+ if Lanes.env.test? && request.env['HTTP_X_TESTING_USER'].present?
14
14
  Lanes::User.where(login: request.env['HTTP_X_TESTING_USER']).first
15
15
  else
16
- Lanes::User.where(id: @session['user_id']).first
16
+ Lanes::User.where(id: request.session['user_id']).first
17
17
  end
18
18
  )
19
19
  end
@@ -23,7 +23,7 @@ module Lanes
23
23
  end
24
24
 
25
25
  def error_message_for_access
26
- return "Unable to " + case @request.request_method
26
+ return "Unable to " + case request.request_method
27
27
  when 'GET' then "read"
28
28
  when 'POST','PATCH','PUT' then "write"
29
29
  when 'DELETE' then "delete"
@@ -34,13 +34,13 @@ module Lanes
34
34
 
35
35
  def allowed_access_to?(klass)
36
36
  return false if current_user.nil?
37
- case @request.request_method
37
+ case request.request_method
38
38
  when 'GET'
39
- klass.can_read_attributes?(@request.params,current_user)
39
+ klass.can_read_attributes?(request.params,current_user)
40
40
  when 'POST','PATCH','PUT'
41
- klass.can_write_attributes?(@request.params,current_user)
41
+ klass.can_write_attributes?(request.params,current_user)
42
42
  when 'DELETE'
43
- klass.can_delete_attributes?(@request.params,current_user)
43
+ klass.can_delete_attributes?(request.params,current_user)
44
44
  else
45
45
  false
46
46
  end
@@ -0,0 +1,64 @@
1
+ # Coffeescript has two shortcomings with regards to Lanes
2
+ #
3
+ # The first is that it's extends format is incompatible with AmpersandState,
4
+ # State does quite a bit more via it's own .extend methods.
5
+ # Accordingly, we substitute our own "extend" call whenever we encounter a coffeescript extends
6
+ #
7
+ # The second issue is that if a constructor isn't present, coffeescript with generate it's own
8
+ # blank implementation that fails to call "super". We add a constructor that does call super if one's missing.
9
+
10
+ module Lanes
11
+ module API
12
+ class CoffeeScriptProcessor < JsAssetCompiler
13
+
14
+ class CoffeeClass
15
+ attr_reader :name, :extends, :contents, :file_contents, :indent
16
+ def initialize(name, extends, file_contents)
17
+ @name=name; @extends=extends; @file_contents=file_contents
18
+ file_contents.gsub!(/class\s+#{name}\s+.*?\n/,"class #{name}\n")
19
+ @contents = @file_contents[/(class #{name}\n.*?)(\n\w|\Z)/m,1]
20
+ @indent = @contents[/\n(\s+)(\w+):/,1] || ' '
21
+ end
22
+
23
+ def ensure_property(property_name, definition)
24
+ if contents !~ /^\s+#{property_name}\s*:/
25
+ # figure out how much to indent, sigh.
26
+ file_contents.gsub!(/class #{name}\n/,
27
+ "\\0#{indent}#{property_name}: #{definition}\n")
28
+ end
29
+ end
30
+
31
+ def save
32
+ file_contents.gsub!( /(class #{name}\n.*?)(\n\w|\Z)/m,
33
+ "\\1\n#{extends}.extend(#{name})\n\\2" )
34
+ end
35
+ end
36
+
37
+ self.registered_extension = '.coffee'
38
+
39
+ attr_reader :contents
40
+
41
+ CONSTRUCTOR = /constructor\s*:/
42
+
43
+ EXTENDING_CLASS_DEFINITION = /class\s+([\w|\.]+)\s+extends\s+([\w|\.]+)\s*?\n/
44
+
45
+ def cleaned
46
+ @contents = data.dup
47
+ data.scan(EXTENDING_CLASS_DEFINITION) do |match|
48
+ (name, extends) = match
49
+ cc = CoffeeClass.new(name, extends, contents)
50
+ cc.ensure_property("constructor", "-> super")
51
+ cc.ensure_property("FILE", "FILE")
52
+ cc.save
53
+ end
54
+ contents
55
+ end
56
+
57
+ def evaluate(scope, locals, &block)
58
+ wrap_js scope, ::CoffeeScript.compile(cleaned, bare: true)
59
+ end
60
+
61
+
62
+ end
63
+ end
64
+ end
@@ -40,47 +40,53 @@ module Lanes
40
40
 
41
41
  end
42
42
 
43
- class CoffeeScriptWrapper < JsAssetCompiler
44
- self.registered_extension = '.coffee'
45
-
46
- CONSTRUCTOR = /constructor\s*:/
47
- EXTENDING_CLASS = /class\s+([\w|\.]+)\s+extends\s+([\w|\.]+)\s*?\n/
48
-
49
- # Coffeescript has two shortcomings with regards to Lanes
50
- #
51
- # The first is that it's extends format is incompatible with AmpersandState,
52
- # State does quite a bit more via it's own .extend methods.
53
- # Accordingly, we substitute our own "extend" call whenever we encounter a coffeescript extends
54
- #
55
- # The second issue is that if a constructor isn't present, coffeescript with generate it's own
56
- # blank implementation that fails to call "super". We add a constructor that does call super if one's missing.
57
-
58
- def cleaned
59
- contents = data.dup
60
- data.scan(EXTENDING_CLASS) do |match|
61
- (name,extends) = match
62
-
63
- contents.gsub!(/class\s+#{name}\s+.*?\n/,"class #{name}\n")
64
- definition = contents[/(class #{name}\n.*?)(\n\w|\Z)/m,1]
65
- # is it missing a constructor?
66
- if definition !~ /constructor:/
67
- # figure out how much to indent, sigh.
68
- indent = definition[/\n(\s+)(\w+):/,1] || ' '
69
- contents.gsub!(/class #{name}\n/,"\\0#{indent}constructor: -> super\n")
70
-
71
- end
72
- contents.gsub!( /(class #{name}\n.*?)(\n\w|\Z)/m, "\\1\n#{extends}.extend(#{name})\n\\2" )
73
-
74
- contents
75
- #contents << "#{extends}.extend(#{name})\n"
76
- end
77
- contents
78
- end
79
-
80
- def evaluate(scope, locals, &block)
81
- wrap_js scope, ::CoffeeScript.compile(cleaned, bare: true)
82
- end
83
- end
43
+ # class CoffeeScriptWrapper < JsAssetCompiler
44
+ # self.registered_extension = '.coffee'
45
+
46
+ # CONSTRUCTOR = /constructor\s*:/
47
+ # CLASS_DEFINITION = /\s*class\s+([\w|\.]+)/
48
+ # EXTENDING_CLASS = /class\s+([\w|\.]+)\s+extends\s+([\w|\.]+)\s*?\n/
49
+
50
+ # # Coffeescript has two shortcomings with regards to Lanes
51
+ # #
52
+ # # The first is that it's extends format is incompatible with AmpersandState,
53
+ # # State does quite a bit more via it's own .extend methods.
54
+ # # Accordingly, we substitute our own "extend" call whenever we encounter a coffeescript extends
55
+ # #
56
+ # # The second issue is that if a constructor isn't present, coffeescript with generate it's own
57
+ # # blank implementation that fails to call "super". We add a constructor that does call super if one's missing.
58
+
59
+ # def cleaned
60
+ # contents = data.dup
61
+ # data.scan(CLASS_DEFINITION) do |match|
62
+ # (name, extends) = match
63
+ # definition = get_definition(name)
64
+
65
+
66
+ # # is it missing a constructor?
67
+ # if definition !~ /constructor:/
68
+ # # figure out how much to indent, sigh.
69
+ # indent = definition[/\n(\s+)(\w+):/,1] || ' '
70
+ # contents.gsub!(/class #{name}\n/,"\\0#{indent}constructor: -> super\n")
71
+
72
+ # end
73
+ # contents.gsub!( /(class #{name}\n.*?)(\n\w|\Z)/m, "\\1\n#{extends}.extend(#{name})\n\\2" )
74
+
75
+ # contents
76
+ # #contents << "#{extends}.extend(#{name})\n"
77
+ # end
78
+ # contents
79
+ # end
80
+
81
+ # def evaluate(scope, locals, &block)
82
+ # wrap_js scope, ::CoffeeScript.compile(cleaned, bare: true)
83
+ # end
84
+
85
+ # def get_definition(name)
86
+ # contents.gsub!(/class\s+#{name}\s+.*?\n/,"class #{name}\n")
87
+ # definition = contents[/(class #{name}\n.*?)(\n\w|\Z)/m,1]
88
+ # end
89
+ # end
84
90
 
85
91
  class Es6Compiler < JsAssetCompiler
86
92
  self.registered_extension = '.es6'
@@ -129,3 +135,5 @@ module Lanes
129
135
 
130
136
  end
131
137
  end
138
+
139
+ require_relative 'coffeescript_processor'
@@ -35,7 +35,9 @@ module Lanes
35
35
  PubSub.initialize(self)
36
36
  Extensions.load_controlling_config
37
37
  # late load in case an extension has provided an alternative implementation
38
- require "lanes/api/null_authentication_provider" unless API.const_defined?(:AuthenticationProvider)
38
+ unless API.const_defined?(:AuthenticationProvider)
39
+ require "lanes/api/null_authentication_provider"
40
+ end
39
41
  # use Rack::Csrf, :skip=>['GET:/'], :raise => true
40
42
  end
41
43
 
@@ -1,6 +1,6 @@
1
1
  module Lanes::Concerns
2
2
 
3
- # Event subscription and publishing for Stockor Models
3
+ # Event subscription and publishing for Models
4
4
  # Every model has certain built-in events (:save, :create, :update, :destroy)
5
5
  # And may also implement custom events that reflect the models domain
6
6
  # @example Send an email when a customer's name is updated
@@ -50,31 +50,37 @@ module Lanes::Concerns
50
50
  class_attribute :_event_listeners
51
51
  base.valid_event_names = [ :save, :create, :update, :destroy ]
52
52
 
53
- base.after_save :fire_after_save_events
54
- base.after_create :fire_after_create_events
55
- base.after_update :fire_after_update_events
56
- base.after_destroy :fire_after_destroy_events
53
+ base.after_save :fire_after_save_pubsub_events
54
+ base.after_create :fire_after_create_pubsub_events
55
+ base.after_update :fire_after_update_pubsub_events
56
+ base.after_destroy :fire_after_destroy_pubsub_events
57
57
  end
58
58
 
59
59
  module ClassMethods
60
60
  def inherited(base)
61
61
  super
62
62
  klass = base.to_s.demodulize
63
-
64
- #p klass
65
- # if klass=="Tester"
66
- # binding.pry
67
- # end
68
63
  events = PubSub::PendingListeners.claim( klass )
69
- events.each{ | name, procs | base.event_listeners[name] += procs }
70
-
64
+ events.each{ | name, procs |
65
+ procs.each{|pr|
66
+ base.send(:_add_event_listener, name, &pr)
67
+ }
68
+ }
71
69
  end
72
70
 
73
71
  def observe( event, &block )
74
- _ensure_validate_event( event )
72
+ unless self.valid_event_names.include?(event.to_sym)
73
+ raise InvalidEvent.new("#{event} is not a valid event for #{self}")
74
+ end
75
75
  _add_event_listener( event.to_sym, &block )
76
76
  end
77
77
 
78
+ protected
79
+
80
+ def has_additional_events( *names )
81
+ self.valid_event_names += names.map{ |name| name.to_sym }
82
+ end
83
+
78
84
  private
79
85
 
80
86
  def _add_event_listener(name, &block)
@@ -84,46 +90,38 @@ module Lanes::Concerns
84
90
  self._event_listeners = self._event_listeners.dup
85
91
  self._event_listeners[name] = listeners
86
92
  end
87
-
88
- def _ensure_validate_event(event)
89
- unless self.valid_event_names.include?(event.to_sym)
90
- raise InvalidEvent.new("#{event} is not a valid event for #{self}")
91
- end
92
- end
93
-
94
- def has_additional_events( *names )
95
- self.valid_event_names += names.map{ |name| name.to_sym }
96
- end
97
93
  end
98
94
 
99
95
  protected
100
96
 
101
- def fire_after_destroy_events
102
- _fire_event(:update, self )
97
+ def fire_after_destroy_pubsub_events
98
+ fire_pubsub_event(:update, self)
103
99
  end
104
- def fire_after_update_events
105
- _fire_event(:update, self )
100
+
101
+ def fire_after_update_pubsub_events
102
+ fire_pubsub_event(:update, self)
106
103
  end
107
- def fire_after_create_events
108
- _fire_event(:create, self )
104
+
105
+ def fire_after_create_pubsub_events
106
+ fire_pubsub_event(:create, self)
109
107
  end
110
- def fire_after_save_events
111
- _fire_event( :save, self )
108
+
109
+ def fire_after_save_pubsub_events
110
+ fire_pubsub_event(:save, self)
112
111
  end
113
112
 
114
- def fire_event( name, *arguments )
115
- self.class._ensure_validate_event( name )
116
- arguments.unshift( self )
117
- _fire_event( name, *arguments )
113
+ def fire_pubsub_event(name, *arguments)
114
+ return if self.class._event_listeners.nil? ||
115
+ !self.class._event_listeners.has_key?(name.to_sym)
116
+ arguments = arguments.dup.unshift(self)
117
+ self.class._event_listeners[ name.to_sym ].each{ | block |
118
+ block.call(*arguments)
119
+ }
118
120
  end
119
121
 
120
122
  private
121
123
 
122
- def _fire_event( name, *arguments )
123
- return if self.class._event_listeners.nil? ||
124
- !self.class._event_listeners.has_key?(name.to_sym)
125
-
126
- self.class._event_listeners[ name.to_sym ].each{ | block | block.call(*arguments) }
124
+ def _fire_pubsub_event( name, *arguments )
127
125
  end
128
126
  end
129
127
 
@@ -51,25 +51,31 @@ module Lanes
51
51
  # Database tables will have this prefix applied to them
52
52
  config_option :table_prefix, ''
53
53
 
54
- config_option :debug_assets, false
55
- config_option :default_javascript_tag_options, {}
56
-
54
+ # The configuration environment to use, test, development, production
57
55
  config_option :environment, (ENV['RACK_ENV'] || 'development').to_sym
56
+
58
57
  # The secret key to use for session cookies.
59
58
  config_option :session_secret_key_base, '1234', silent: true
59
+
60
+ # Configuration for Redis
60
61
  config_option :redis, { path: "/tmp/redis.sock" }
61
- config_option :pubsub_key, nil
62
- config_option :pubsub_host, nil
63
- config_option :pubsub_timeout, 10
64
62
 
65
- config_option :es6_transpiler_path, '6to5'
66
- config_option :es6_transpiler_options, '-t '
63
+ # Title of application
67
64
  config_option :app_title, "Lanes Test"
65
+
66
+ # url prefix to use for assets
68
67
  config_option :assets_path_prefix, "/assets"
68
+
69
+ # prefix to use for all urls
69
70
  config_option :mounted_at, ''
71
+
72
+ # The initial view class to display
70
73
  config_option :root_view, 'Lanes.Workspace.Layout'
74
+
75
+ # Screen to display on load (if workspace extension is used)
71
76
  config_option :initial_workspace_screen_id, ''
72
- config_option :specs_root, Pathname.getwd
77
+
78
+ # types of assets to include into compiled package
73
79
  config_option :static_asset_types, ['images','fonts']
74
80
  end
75
81
 
data/lib/lanes/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Lanes
2
2
 
3
- VERSION = "0.1.7"
3
+ VERSION = "0.1.8"
4
4
 
5
5
  end
@@ -1,6 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem "lanes", '0.1.6'
3
+ gem "lanes", '0.1.8'
4
4
 
5
5
  gem "rake"
6
6
  gem 'puma'
@@ -1,6 +1,5 @@
1
1
  class AppyApp.Extension extends Lanes.Extensions.Base
2
2
 
3
- FILE: FILE
4
3
 
5
4
  identifier: "appy-app"
6
5
 
@@ -1,6 +1,5 @@
1
1
  class AppyApp.Models.TestTest extends AppyApp.Models.Base
2
2
 
3
- FILE: FILE
4
3
 
5
4
  props:
6
5
  id : "integer"
@@ -1,6 +1,5 @@
1
1
  class AppyApp.Screens.ReadySetGo extends AppyApp.Screens.Base
2
2
 
3
- FILE: FILE
4
3
 
5
4
  subviews: {}
6
5
 
@@ -1,4 +1,5 @@
1
1
  Lanes::Screen.define "ready-set-go" do | screen |
2
+ screen.title = "Ready Set Go"
2
3
  screen.description = ""
3
4
  screen.icon = ""
4
5
  screen.group_id = ""
@@ -1,6 +1,5 @@
1
1
  class AppyApp.Views.BigView extends AppyApp.Views.Base
2
2
 
3
- FILE: FILE
4
3
 
5
4
  ## These are listed only as examples.
6
5
  # If unused they can be safely removed.
@@ -1,6 +1,6 @@
1
1
  require "lanes/spec_helper"
2
2
 
3
- class JavascriptProcessorTest < Lanes::TestCase
3
+ class CoffeeScriptProcessor < Lanes::TestCase
4
4
 
5
5
  SCRIPT = <<-EOS
6
6
  class NS.Baz
@@ -30,6 +30,7 @@ class NS.Baz
30
30
  alert(msg);
31
31
 
32
32
  class NS.Bar
33
+ FILE: FILE
33
34
  constructor: -> super
34
35
  squawk:->
35
36
  this.alert("Hello World!")
@@ -37,6 +38,7 @@ class NS.Bar
37
38
  NS.Baz.extend(NS.Bar)
38
39
 
39
40
  class Foo
41
+ FILE: FILE
40
42
  constructor: ->
41
43
  this.called=true
42
44
  super
@@ -65,6 +67,8 @@ NS.Baz = (function() {
65
67
  })();
66
68
 
67
69
  NS.Bar = (function() {
70
+ Bar.prototype.FILE = FILE;
71
+
68
72
  function Bar() {
69
73
  Bar.__super__.constructor.apply(this, arguments);
70
74
  }
@@ -80,6 +84,8 @@ NS.Bar = (function() {
80
84
  NS.Baz.extend(NS.Bar);
81
85
 
82
86
  Foo = (function() {
87
+ Foo.prototype.FILE = FILE;
88
+
83
89
  function Foo() {
84
90
  this.called = true;
85
91
  Foo.__super__.constructor.apply(this, arguments);
@@ -101,7 +107,7 @@ EOS
101
107
  Scope = Struct.new(:logical_path)
102
108
 
103
109
  def test_coffeescript_generation
104
- template = API::CoffeeScriptWrapper.new{ |t| SCRIPT }
110
+ template = API::CoffeeScriptProcessor.new{ |t| SCRIPT }
105
111
  assert_equal CLEANED, template.cleaned
106
112
 
107
113
 
@@ -5,123 +5,122 @@ describe "PubSub" do
5
5
  include ActiveRecordMocks::IncludeMe
6
6
  around do | test |
7
7
  with_mocked_tables do |m|
8
- m.create_table do |t|
9
- t.model_name :Evbt
10
- t.belongs_to :evhm, class_name: "Evhm", listen: { create: 'on_create_hm' },
11
- inverse_of: :evbt
12
- t.layout do |l|
13
- l.integer :evhm_id
14
- end
15
- t.parent_class "Lanes::Model"
16
- end
17
- class Evbt
8
+
9
+ class A < Lanes::Model
18
10
  has_additional_events :test_one
19
- def on_create_hm(*args)
20
- 32
11
+ def on_save_b(*args)
12
+ end
13
+ def on_save_event_tester(*args)
21
14
  end
22
15
  end
23
-
24
16
  m.create_table do |t|
25
- t.model_name :EventTester
17
+ t.model_name :A
26
18
  t.parent_class "Lanes::Model"
27
- t.belongs_to :evbt, class_name: 'Evhm'
19
+ t.belongs_to :b_model, class_name: "B", listen: { save: :on_save_b },
20
+ inverse_of: :a_model
28
21
 
29
- t.has_many :evhm, class_name: 'Evhm', inverse_of: :event_tester
22
+ t.belongs_to :event_tester, class_name: "EventTester", inverse_of: :a_models,
23
+ listen: { save: 'on_save_event_tester' }
30
24
 
31
25
  t.layout do |l|
32
- l.integer :bt_id
33
- l.string :name, :number
26
+ l.integer :b_model_id, :b_id
27
+ l.integer :event_tester_id
34
28
  end
35
29
  end
36
30
 
31
+ class B < Lanes::Model
32
+ def on_save_a(*args)
33
+ end
34
+ end
37
35
  table = m.create_table do |t|
38
- t.model_name :Evhm
39
- t.belongs_to :evbt, class_name: "Evbt", listen: { save: 'on_create_bt' },
40
- inverse_of: :evhm
41
- t.belongs_to :event_tester, class_name: "EventTester", inverse_of: :evhm,
42
- listen: { save: 'on_save_event_tester' }
36
+ t.model_name :B
43
37
  t.parent_class "Lanes::Model"
38
+ t.has_one :a_model, class_name: "A", listen: { save: 'on_save_a' },
39
+ inverse_of: :b_model
44
40
  t.layout do | l |
45
41
  l.string :mumble
46
- l.integer :evbt_id, :event_tester_id
47
42
  end
48
43
  end
49
- def table.on_save_event_tester(*args)
44
+
45
+ class EventTester < Lanes::Model
50
46
  end
51
47
 
48
+ m.create_table do |t|
49
+ t.model_name :EventTester
50
+ t.parent_class "Lanes::Model"
51
+ t.has_many :a_models, class_name: 'A', inverse_of: :event_tester
52
52
 
53
+ t.layout do |l|
54
+ l.string :name, :number
55
+ end
56
+ end
53
57
 
54
58
  test.call
59
+
55
60
  end
56
61
  end
57
62
 
58
63
  it "listens to other associations" do
59
- e=Evbt.new
60
- assert_equal 32, e.on_create_hm
61
- b=e.build_evhm
62
- b.expects(:on_create_bt)
63
- assert e.save
64
+ a=A.new
65
+ b=a.build_b_model
66
+ assert b.a_model
67
+ a.expects(:on_save_b)
68
+ assert a.save
64
69
 
65
70
  et = EventTester.new
66
- evh = et.evhm.build
67
- evh.expects(:on_save_event_tester)
68
- assert et.save
71
+ a=et.a_models.build
72
+ a.expects(:on_save_event_tester)
73
+ et.save
69
74
  end
70
75
 
71
76
  it "registers" do
72
- assert_equal [ :save, :create, :update, :destroy ],EventTester.valid_event_names
73
- assert_equal [ :save, :create, :update, :destroy, :test_one ],Evbt.valid_event_names
77
+ assert_equal [ :save, :create, :update, :destroy ], EventTester.valid_event_names
78
+ assert_equal [ :save, :create, :update, :destroy, :test_one ], A.valid_event_names
74
79
  end
75
80
 
76
81
  it "can only observe valid events" do
77
- assert_raises( EventTester::InvalidEvent) do
82
+ assert_raises(EventTester::InvalidEvent) do
78
83
  EventTester.observe(:invalid_event) do | ev |
79
84
  end
80
85
  end
81
- assert_raises( EventTester::InvalidEvent) do
82
- EventTester.observe(:save) do | ev |
86
+ assert_raises(EventTester::InvalidEvent) do
87
+ B.observe(:test_two) do | ev |
83
88
  end
84
- EventTester.new.send(:fire_event,:invalid_event)
85
89
  end
86
- assert_raises( EventTester::InvalidEvent) do
87
- Evbt.observe(:test_two) do | ev |
88
- end
89
- end
90
- assert_raises( EventTester::InvalidEvent) do
91
- Evhm.observe(:test_one) do | ev |
90
+ assert_raises(EventTester::InvalidEvent) do
91
+ A.observe(:glub_glub) do | ev |
92
92
  end
93
93
  end
94
94
  begin
95
- Evbt.observe(:test_one) do | ev |
95
+ B.observe(:test_one) do | ev |
96
96
  end
97
97
  rescue EventTester::InvalidEvent=>e
98
- assert_equal 'test_one is not a valid event for PubSubTest::Ev2', e.to_s
98
+ assert_equal 'test_one is not a valid event for B', e.to_s
99
99
  end
100
100
  end
101
101
 
102
102
  it "events can be subscribed to" do
103
103
  EventTester.observe(:save) do | ev |
104
-
105
104
  end
106
105
  end
107
106
 
108
107
  it "can be fired and observed" do
109
108
  results=[]
110
- Evbt.observe(:test_one) do | ev, one, two |
109
+ A.observe(:test_one) do | ev, one, two |
111
110
  results = [ ev, one, two ]
112
111
  end
113
- evt=Evbt.new
114
- evt.send(:fire_event, :test_one, 3, 5 )
115
- assert_equal [ evt, 3, 5 ], results
112
+ a=A.new
113
+ a.send(:fire_pubsub_event, :test_one, 3, 5 )
114
+ assert_equal [a, 3, 5], results
116
115
 
117
- evt.send(:fire_event, :test_one, 'foo' )
118
- assert_equal [ evt, 'foo', nil ], results
116
+ a.send(:fire_pubsub_event, :test_one, 'foo' )
117
+ assert_equal [a, 'foo', nil], results
119
118
  end
120
119
 
121
120
 
122
121
  it "can use custom event assertions" do
123
- assert_event_fires( Evbt, :test_one ) do
124
- Evbt.new.send(:fire_event, :test_one, 3, 5 )
122
+ assert_event_fires(A, :test_one) do
123
+ A.new.send(:fire_pubsub_event, :test_one, 3, 5)
125
124
  end
126
125
  assert_equal [ 3, 5 ], last_event_results[1..-1]
127
126
  end
@@ -1,6 +1,5 @@
1
1
  class <%= namespace %>.Extension extends Lanes.Extensions.Base
2
2
 
3
- FILE: FILE
4
3
 
5
4
  identifier: "<%= identifier %>"
6
5
 
@@ -1,6 +1,5 @@
1
1
  class <%= namespace %>.Models.<%= class_name %> extends <%= namespace %>.Models.Base
2
2
 
3
- FILE: FILE
4
3
 
5
4
  props:
6
5
  <% fields.each do |field| -%>
@@ -1,6 +1,5 @@
1
1
  class <%= namespace.camelize %>.Screens.<%= class_name %> extends <%= namespace.camelize %>.Screens.Base
2
2
 
3
- FILE: FILE
4
3
  <% if @template -%>
5
4
  template: '''
6
5
  <%= @template %>
@@ -1,6 +1,5 @@
1
1
  class <%= namespace %>.Views.<%= class_name %> extends <%= namespace %>.Views.Base
2
2
 
3
- FILE: FILE
4
3
 
5
4
  ## These are listed only as examples.
6
5
  # If unused they can be safely removed.
@@ -1,4 +1,5 @@
1
1
  Lanes::Screen.define "<%= screen_id %>" do | screen |
2
+ screen.title = "<%= options[:title] %>"
2
3
  screen.description = "<%= options[:description] %>"
3
4
  screen.icon = "<%= options[:icon] %>"
4
5
  screen.group_id = "<%= options[:group] %>"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lanes
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Stitt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-17 00:00:00.000000000 Z
11
+ date: 2015-03-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -527,6 +527,8 @@ files:
527
527
  - client/lanes/components/select-field/SelectField.coffee
528
528
  - client/lanes/components/select-field/index.js
529
529
  - client/lanes/components/select-field/styles.scss
530
+ - client/lanes/components/select2/Select2.coffee
531
+ - client/lanes/components/select2/index.js
530
532
  - client/lanes/extension/Base.coffee
531
533
  - client/lanes/extension/EarlyExtensions.js.erb
532
534
  - client/lanes/extension/Extensions.coffee
@@ -810,6 +812,7 @@ files:
810
812
  - lib/lanes/access/user.rb
811
813
  - lib/lanes/access/version.rb
812
814
  - lib/lanes/api.rb
815
+ - lib/lanes/api/coffeescript_processor.rb
813
816
  - lib/lanes/api/controller.rb
814
817
  - lib/lanes/api/eco.js
815
818
  - lib/lanes/api/error_formatter.rb
@@ -886,7 +889,6 @@ files:
886
889
  - npm-build/package.json
887
890
  - npm-build/shims/underscore.js
888
891
  - npm-build/template.js
889
- - spec/api/javascript_processor_spec.rb
890
892
  - spec/command-reference-files/initial/.gitignore
891
893
  - spec/command-reference-files/initial/Gemfile
892
894
  - spec/command-reference-files/initial/Guardfile
@@ -943,6 +945,7 @@ files:
943
945
  - spec/lanes/models/PubSubSpec.coffee
944
946
  - spec/lanes/views/BaseSpec.coffee
945
947
  - spec/lanes/views/FormBindingsSpec.coffee
948
+ - spec/server/api/coffeescript_processor_spec.rb
946
949
  - spec/server/command_spec.rb
947
950
  - spec/server/concerns/api_path_spec.rb
948
951
  - spec/server/concerns/association_extensions_spec.rb