lanes 0.1.6 → 0.1.7

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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/.dir-locals.el +8 -0
  3. data/client/lanes/Config.coffee +1 -1
  4. data/client/lanes/access/Extension.coffee +1 -6
  5. data/client/lanes/access/screens/user-management/GridUserEditor.coffee +6 -5
  6. data/client/lanes/access/screens/user-management/grid-popover-editor.html +2 -2
  7. data/client/lanes/components/grid/vendor/jquery.dataTables.js +1 -1
  8. data/client/lanes/components/multi-select/MultiSelect.coffee +10 -5
  9. data/client/lanes/components/select-field/SelectField.coffee +2 -1
  10. data/client/lanes/models/Base.coffee +1 -1
  11. data/client/lanes/models/PubSub.coffee +4 -3
  12. data/client/lanes/models/Sync.coffee +3 -3
  13. data/client/lanes/screens/ChangeListener.coffee +0 -3
  14. data/client/lanes/testing/ModelSaver.coffee +12 -1
  15. data/client/lanes/views/Base.coffee +1 -1
  16. data/client/lanes/views/Helpers.coffee +2 -1
  17. data/client/lanes/views/SaveNotify.coffee +9 -7
  18. data/client/lanes/views/TimedMask.coffee +1 -1
  19. data/lanes.gemspec +1 -1
  20. data/lib/lanes/access/authentication_provider.rb +17 -11
  21. data/lib/lanes/access/config/routes.rb +1 -1
  22. data/lib/lanes/access/extension.rb +9 -2
  23. data/lib/lanes/access/role.rb +12 -0
  24. data/lib/lanes/access.rb +8 -8
  25. data/lib/lanes/api/null_authentication_provider.rb +2 -2
  26. data/lib/lanes/api/request_wrapper.rb +10 -10
  27. data/lib/lanes/concerns/association_extensions.rb +16 -7
  28. data/lib/lanes/concerns/pub_sub.rb +55 -30
  29. data/lib/lanes/guard_tasks.rb +7 -3
  30. data/lib/lanes/version.rb +1 -1
  31. data/{appy-app → spec/command-reference-files/initial}/.gitignore +0 -0
  32. data/{appy-app → spec/command-reference-files/initial}/Gemfile +1 -1
  33. data/{appy-app → spec/command-reference-files/initial}/Guardfile +0 -0
  34. data/{appy-app → spec/command-reference-files/initial}/Rakefile +0 -0
  35. data/{appy-app → spec/command-reference-files/initial}/client/appy-app/Extension.coffee +0 -0
  36. data/{appy-app → spec/command-reference-files/initial}/client/appy-app/Router.coffee +0 -0
  37. data/{appy-app → spec/command-reference-files/initial}/client/appy-app/components/.gitkeep +0 -0
  38. data/{appy-app → spec/command-reference-files/initial}/client/appy-app/controllers/.gitkeep +0 -0
  39. data/{appy-app → spec/command-reference-files/initial}/client/appy-app/index.js +0 -0
  40. data/{appy-app → spec/command-reference-files/initial}/client/appy-app/models/.gitkeep +0 -0
  41. data/{appy-app → spec/command-reference-files/initial}/client/appy-app/models/Base.coffee +0 -0
  42. data/{appy-app → spec/command-reference-files/initial}/client/appy-app/screens/.gitkeep +0 -0
  43. data/spec/{server/command-reference-files → command-reference-files}/initial/client/appy-app/screens/Base.coffee +0 -0
  44. data/{appy-app → spec/command-reference-files/initial}/client/appy-app/styles.scss +0 -0
  45. data/{appy-app → spec/command-reference-files/initial}/client/appy-app/views/.gitkeep +0 -0
  46. data/{appy-app → spec/command-reference-files/initial}/client/appy-app/views/Base.coffee +0 -0
  47. data/{appy-app → spec/command-reference-files/initial}/config/database.yml +0 -0
  48. data/{appy-app → spec/command-reference-files/initial}/config/lanes.rb +0 -0
  49. data/{appy-app → spec/command-reference-files/initial}/config/routes.rb +0 -0
  50. data/spec/{server/command-reference-files → command-reference-files}/initial/config/screens.rb +0 -0
  51. data/{appy-app → spec/command-reference-files/initial}/config.ru +0 -0
  52. data/{appy-app → spec/command-reference-files/initial}/db/.gitkeep +0 -0
  53. data/{appy-app → spec/command-reference-files/initial}/lib/appy-app/extension.rb +0 -0
  54. data/{appy-app → spec/command-reference-files/initial}/lib/appy-app/model.rb +0 -0
  55. data/{appy-app → spec/command-reference-files/initial}/lib/appy-app/models/empty.rb +0 -0
  56. data/{appy-app → spec/command-reference-files/initial}/lib/appy-app/version.rb +0 -0
  57. data/spec/{server/command-reference-files → command-reference-files}/initial/lib/appy-app.rb +0 -0
  58. data/{appy-app → spec/command-reference-files/initial}/spec/appy-app/helpers/AppyAppHelpers.coffee +0 -0
  59. data/{appy-app → spec/command-reference-files/initial}/spec/appy-app/screens/Base.coffee +0 -0
  60. data/{appy-app → spec/command-reference-files/initial}/spec/server/spec_helpers.rb +0 -0
  61. data/spec/{server/command-reference-files → command-reference-files}/model/client/appy-app/models/TestTest.coffee +0 -0
  62. data/spec/{server/command-reference-files → command-reference-files}/model/config/routes.rb +0 -0
  63. data/spec/{server/command-reference-files → command-reference-files}/model/db/migrate/20150218032025_create_test_tests.rb +0 -0
  64. data/spec/{server/command-reference-files → command-reference-files}/model/lib/appy-app/models/test_test.rb +0 -0
  65. data/spec/{server/command-reference-files → command-reference-files}/model/spec/appy-app/models/TestTestSpec.coffee +0 -0
  66. data/spec/{server/command-reference-files → command-reference-files}/model/spec/fixtures/appy-app/test_test.yml +0 -0
  67. data/spec/{server/command-reference-files → command-reference-files}/model/spec/server/test_test_spec.rb +0 -0
  68. data/spec/{server/command-reference-files → command-reference-files}/screen/client/appy-app/screens/ready-set-go/ReadySetGo.coffee +0 -0
  69. data/spec/{server/command-reference-files → command-reference-files}/screen/client/appy-app/screens/ready-set-go/index.js +0 -0
  70. data/spec/{server/command-reference-files → command-reference-files}/screen/client/appy-app/screens/ready-set-go/index.scss +0 -0
  71. data/spec/{server/command-reference-files → command-reference-files}/screen/client/appy-app/screens/ready-set-go/layout.html +0 -0
  72. data/spec/{server/command-reference-files → command-reference-files}/screen/config/screens.rb +0 -0
  73. data/spec/{server/command-reference-files → command-reference-files}/screen/spec/appy-app/screens/ready-set-go/ReadySetGoSpec.coffee +0 -0
  74. data/spec/{server/command-reference-files → command-reference-files}/view/client/appy-app/views/BigView.coffee +0 -0
  75. data/spec/{server/command-reference-files → command-reference-files}/view/spec/appy-app/views/BigViewSpec.coffee +0 -0
  76. data/spec/lanes/models/BaseSpec.coffee +4 -4
  77. data/spec/server/command_spec.rb +5 -1
  78. data/spec/server/concerns/association_extensions_spec.rb +0 -8
  79. data/spec/server/concerns/pub_sub_spec.rb +77 -31
  80. data/spec/server/spec_helper.rb +1 -0
  81. metadata +48 -91
  82. data/appy-app/client/appy-app/screens/Base.coffee +0 -10
  83. data/appy-app/config/screens.rb +0 -8
  84. data/appy-app/lib/appy-app.rb +0 -14
  85. data/spec/server/command-reference-files/initial/.gitignore +0 -3
  86. data/spec/server/command-reference-files/initial/Gemfile +0 -6
  87. data/spec/server/command-reference-files/initial/Guardfile +0 -13
  88. data/spec/server/command-reference-files/initial/Rakefile +0 -2
  89. data/spec/server/command-reference-files/initial/client/appy-app/Extension.coffee +0 -7
  90. data/spec/server/command-reference-files/initial/client/appy-app/Router.coffee +0 -4
  91. data/spec/server/command-reference-files/initial/client/appy-app/components/.gitkeep +0 -0
  92. data/spec/server/command-reference-files/initial/client/appy-app/controllers/.gitkeep +0 -0
  93. data/spec/server/command-reference-files/initial/client/appy-app/index.js +0 -21
  94. data/spec/server/command-reference-files/initial/client/appy-app/models/.gitkeep +0 -0
  95. data/spec/server/command-reference-files/initial/client/appy-app/models/Base.coffee +0 -5
  96. data/spec/server/command-reference-files/initial/client/appy-app/screens/.gitkeep +0 -0
  97. data/spec/server/command-reference-files/initial/client/appy-app/styles.scss +0 -1
  98. data/spec/server/command-reference-files/initial/client/appy-app/views/.gitkeep +0 -0
  99. data/spec/server/command-reference-files/initial/client/appy-app/views/Base.coffee +0 -5
  100. data/spec/server/command-reference-files/initial/config/database.yml +0 -9
  101. data/spec/server/command-reference-files/initial/config/lanes.rb +0 -7
  102. data/spec/server/command-reference-files/initial/config/routes.rb +0 -2
  103. data/spec/server/command-reference-files/initial/config.ru +0 -5
  104. data/spec/server/command-reference-files/initial/db/.gitkeep +0 -0
  105. data/spec/server/command-reference-files/initial/lib/appy-app/extension.rb +0 -13
  106. data/spec/server/command-reference-files/initial/lib/appy-app/model.rb +0 -11
  107. data/spec/server/command-reference-files/initial/lib/appy-app/models/empty.rb +0 -0
  108. data/spec/server/command-reference-files/initial/lib/appy-app/version.rb +0 -3
  109. data/spec/server/command-reference-files/initial/spec/appy-app/helpers/AppyAppHelpers.coffee +0 -5
  110. data/spec/server/command-reference-files/initial/spec/appy-app/screens/Base.coffee +0 -5
  111. data/spec/server/command-reference-files/initial/spec/server/spec_helpers.rb +0 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dcfa482b3905ad3ec30faf5fe9569f6f6bc8650b
4
- data.tar.gz: fd43ec7fc591b2f8310f89bf0c734bdff7be1a9f
3
+ metadata.gz: 09c525f4869a2e1f8b1d4ff92ca1f31344715994
4
+ data.tar.gz: 98893dd650c0d95209e9b337623f1c079e807ae6
5
5
  SHA512:
6
- metadata.gz: 953ecd6093ed5e2cdf9e7b29de6f444a82a5299833c1c3166e930047ab8b80bbbf2c7a166d7a08df882a6d26552cf84ffbb577aac4a7299739144eda0708e591
7
- data.tar.gz: 903218ab0ea98c8c1e290ba9c9976ad5585f84080625433e84e6ed22020e036db9d9263a57afc1ec52d039b1c57ad8b43467b9ba9a1021e8f70154d0210d1fe5
6
+ metadata.gz: e8a11e0a3028195b5cf5bf3ff6f1e2cc06754e08e776100fcc69abf330cf1cadcf1c8e8b72d408b60f73266bd719feebc0d523b4415a847957cc7f9d947ef732
7
+ data.tar.gz: a813d60b1ad735443aa90e5461743836f0eb606c81464e61e6d1131dc9e911fb5faba4a345ca876a474c3104205f360f19f1edaf0bb376bcbbe42b1f3fd56d91
data/.dir-locals.el ADDED
@@ -0,0 +1,8 @@
1
+ ((nil . ((js-indent-level . 4)
2
+ (js2-basic-offset . 4)
3
+ (enh-ruby-indent-level . 4)
4
+ (ruby-indent-level . 4)
5
+ (fill-column . 120))))
6
+
7
+ ((js2-mode . ((js2-basic-offset . 4)
8
+ (indent-tabs-mode . nil))))
@@ -3,7 +3,7 @@ class Config extends Lanes.Models.State
3
3
 
4
4
  session:
5
5
  csrf_token: { type: 'string', setOnce: true }
6
- api_path: { type: 'string', setOnce: true }
6
+ api_path: { type: 'string', default: '', setOnce: true }
7
7
  environment: { type: 'string', setOnce: true }
8
8
 
9
9
  derived:
@@ -4,12 +4,7 @@ class Lanes.Access.Extension extends Lanes.Extensions.Base
4
4
 
5
5
  setBootstrapData: (data)->
6
6
  Lanes.current_user = new Lanes.Models.User
7
-
8
- Lanes.Models.Roles.all = new Lanes.Models.Roles(
9
- _.map( data.roles, (role)->
10
- { id: role.toLowerCase(), name: role }
11
- )
12
- )
7
+ Lanes.Models.Roles.all = new Lanes.Models.Roles( data.roles )
13
8
  if data.user
14
9
  Lanes.current_user.set(data.user)
15
10
  if data.access
@@ -2,7 +2,7 @@ class Lanes.Access.Screens.UserManagement.GridUserEditor extends Lanes.Component
2
2
 
3
3
  writeTemplateName: 'user-management/grid-popover-editor'
4
4
  templatePrefix: 'lanes/access/screens'
5
-
5
+ useFormBindings: true
6
6
  writeTemplateData: ->
7
7
  { columns: _.reject(this.grid.columnDefinitions,(f)-> f.field=='role_names') }
8
8
 
@@ -13,10 +13,11 @@ class Lanes.Access.Screens.UserManagement.GridUserEditor extends Lanes.Component
13
13
  options: 'roleOptions'
14
14
 
15
15
  roleOptions: ->
16
- { multiple: true, data: this.model.allRoles, mappings:{ title: 'name', selected: 'member' } }
17
-
18
- constructor: (options={})->
19
- super( _.extend(options,{formBindings: true}) )
16
+ {
17
+ field_name: 'role_names'
18
+ multiple: true, choices: this.model.allRoles
19
+ mappings:{ title: 'name', selected: 'member' }
20
+ }
20
21
 
21
22
  persistFields: ->
22
23
  @model.set({
@@ -1,9 +1,9 @@
1
1
  <form class="user-edit form-horizontal" role="form">
2
2
  <% for field in @columns: %>
3
3
  <div class="form-group">
4
- <label for="field-<%= field.field %>" class="col-sm-3 control-label"><%= field.title %></label>
4
+ <label for="field-<%= field.id %>" class="col-sm-3 control-label"><%= field.title %></label>
5
5
  <div class="col-sm-9">
6
- <input type="text" name="<%= field.field %>" class="form-control" id="field-<%= field.field %>" placeholder="">
6
+ <input type="text" name="<%= field.id %>" class="form-control" id="field-<%= field.id %>" placeholder="">
7
7
  </div>
8
8
  </div>
9
9
  <% end %>
@@ -2652,6 +2652,7 @@
2652
2652
  var compat = function ( old, modern ) {
2653
2653
  return json[old] !== undefined ? json[old] : json[modern];
2654
2654
  };
2655
+ var data = _fnAjaxDataSrc( settings, json );
2655
2656
 
2656
2657
  var draw = compat( 'sEcho', 'draw' );
2657
2658
  var recordsTotal = compat( 'iTotalRecords', 'recordsTotal' );
@@ -2669,7 +2670,6 @@
2669
2670
  settings._iRecordsTotal = parseInt(recordsTotal, 10);
2670
2671
  settings._iRecordsDisplay = parseInt(recordsFiltered, 10);
2671
2672
 
2672
- var data = _fnAjaxDataSrc( settings, json );
2673
2673
  for ( var i=0, ien=data.length ; i<ien ; i++ ) {
2674
2674
  _fnAddData( settings, data[i] );
2675
2675
  }
@@ -26,21 +26,26 @@ class Lanes.Components.MultiSelect extends Lanes.Components.Base
26
26
  if options.choices
27
27
  @selections ||= options.choices
28
28
 
29
- @selections.ensureLoaded()
29
+ @selections?.ensureLoaded?()
30
30
 
31
31
  @mappings = _.extend({
32
32
  id: 'id', title: 'title', selected: 'selected'
33
33
  },options.mappings||{})
34
34
 
35
+ unSelectAll: ->
36
+ this.$el.find(":selected").prop('selected',false)
35
37
 
36
38
  onModelChange: ->
37
- if @selections
39
+ this.unSelectAll()
40
+ if @selections && @model
38
41
  this.onModelAttributeChange(@model,@model.get(@field_name))
39
42
 
40
43
  selectionForID: (id)->
41
- q={}; q[@mappings.id]=parseInt(id)
44
+ q={}; q[@mappings.id]=id
42
45
  @selections.findWhere( q )
43
46
 
44
- onModelAttributeChange: (model,fkid)->
47
+ onModelAttributeChange: (model,fkids)->
45
48
  if ! this.el.binding_is_setting
46
- this.select( this.selectionForID( fkid ) )
49
+ _.each(fkids, (fkid)=>
50
+ @select( @selectionForID( fkid ) )
51
+ )
@@ -51,6 +51,7 @@ class Lanes.Components.SelectField extends Lanes.Components.MultiSelect
51
51
  readTemplate: ->
52
52
  "<div class='ro-input' name='#{this.field_name}'></div>"
53
53
 
54
+
54
55
  select: (option)->
55
56
  if this.readOnly
56
57
  this.$el.text( if option then option.code else "" )
@@ -59,4 +60,4 @@ class Lanes.Components.SelectField extends Lanes.Components.MultiSelect
59
60
  option = this.query("option[value=\"#{id}\"]")
60
61
  option.selected = true
61
62
  else
62
- this.$el.find(":selected").prop('selected',false)
63
+ this.unSelectAll()
@@ -59,7 +59,7 @@ class BaseModel
59
59
 
60
60
  # is the record saved
61
61
  isPersistent: ->
62
- !!( this.api_path() && !this.isNew() )
62
+ !!( _.result(this,'api_path') && !this.isNew() )
63
63
 
64
64
  # used by PubSub to record a remote change to the model
65
65
  addChangeSet: (change)->
@@ -9,7 +9,7 @@ class ModelType extends Lanes.Models.State
9
9
  records: 'object'
10
10
 
11
11
  subscribe: (model)->
12
- channel = "/#{model.api_path()}/#{model.id}"
12
+ channel = "/#{_.result(model,'api_path')}/#{model.id}"
13
13
  Lanes.Vendor.MessageBus.subscribe(channel,(changes)->
14
14
  model.addChangeSet(changes)
15
15
  )
@@ -33,7 +33,8 @@ class ModelTypesCollection extends Lanes.Models.BasicCollection
33
33
  model: ModelType
34
34
 
35
35
  forModel: (model)->
36
- models = this.get(model.api_path()) || this.add(id: model.api_path())
36
+ path = _.result(model,'api_path')
37
+ models = this.get(path) || this.add(id: path)
37
38
 
38
39
 
39
40
  Lanes.Models.PubSub = {
@@ -51,7 +52,7 @@ Lanes.Models.PubSub = {
51
52
  @types.forModel(model).remove(model)
52
53
 
53
54
  instanceFor: ( model_klass, id )->
54
- @types.get(model_klass.prototype.api_path())?.records[id]?.model
55
+ @types.get(_.result(model_klass.prototype,'api_path'))?.records[id]?.model
55
56
 
56
57
  clear: ->
57
58
  @types = new ModelTypesCollection
@@ -77,9 +77,9 @@ Lanes.Models.Sync = {
77
77
  # Ensure that we have a URL.
78
78
  params.url = _.result(model, "url") or Lanes.Models.Sync.urlError() unless options.url
79
79
  params.url += '.json'
80
- params.headers = {
81
- X_CSRF_TOKEN: Lanes.config.csrf_token
82
- }
80
+
81
+ params.headers = _.extend({}, _.result(options,'headers'))
82
+ params.headers['X_CSRF_TOKEN'] = Lanes.config.csrf_token
83
83
  params.contentType = "application/json"
84
84
  if options.data || _.contains(['create','update','patch'], method)
85
85
  params.data = JSON.stringify( options.data || model.dataForSave(options) )
@@ -40,7 +40,4 @@ Lanes.Screens.ChangeListener = {
40
40
  this.changes.invoke('updateTimeAgo')
41
41
  this.$('.changes-notification .scroller').animate({ scrollTop: 0 })
42
42
 
43
- # included: ->
44
- # console.log this
45
-
46
43
  }
@@ -1,14 +1,21 @@
1
1
  class Lanes.Testing.ModelSaver
2
2
 
3
+ @setUser: (login)->
4
+ Lanes.Testing.ModelSaver::headers['X_TESTING_USER']= login
5
+
3
6
  @perform: (model,completion)->
4
7
  saver = new Lanes.Testing.ModelSaver(completion)
5
8
  saver.save(model)
6
9
 
10
+ headers:
11
+ X_ROLLBACK_AFTER_REQUEST: true
12
+
7
13
  constructor: (@completion)->
8
14
  _.bindAll(this,'success','error')
9
15
  spyOn(this, 'success').and.callThrough()
10
16
  spyOn(this, 'error').and.callThrough()
11
17
 
18
+
12
19
  success: ->
13
20
  this.notification.resolve(this)
14
21
  _.defer(@completion) if @completion
@@ -18,6 +25,10 @@ class Lanes.Testing.ModelSaver
18
25
  _.defer(@completion) if @completion
19
26
 
20
27
  save: (model)->
21
- model.save(this)
28
+ model.save(this.toOptions()).then(Lanes.emptyFn, Lanes.emptyFn)
22
29
  this.notification = new _.DeferredPromise
23
30
  this.notification.promise
31
+
32
+
33
+ toOptions: ->
34
+ _.pick(this,'headers','success','error')
@@ -369,7 +369,7 @@ class ViewBase
369
369
  prev = this.previous('model')
370
370
  return if prev == @model
371
371
  this._unbindFromObject(prev, @modelEvents) if prev
372
- this._bindToObject(@model, @modelEvents)
372
+ this._bindToObject(@model, @modelEvents) if @model
373
373
  this.onModelChange?()
374
374
  true
375
375
 
@@ -57,8 +57,9 @@ Lanes.Views.Helpers = {
57
57
  impl.grid_widths(widths);
58
58
 
59
59
  text_field: (name,options={})->
60
+ value = if options.value then " value=#{options.value}" else ''
60
61
  impl.field(name, options,
61
- "<input type='#{options.type || 'text'}' name='#{name}' value='#{options.value}'><span class='feedback'></span>"
62
+ "<input type='#{options.type || 'text'}' name='#{name}'#{value}><span class='feedback'></span>"
62
63
  "<div class='ro-input update' name='#{name}'></div>"
63
64
  )
64
65
 
@@ -1,26 +1,27 @@
1
-
2
1
  class ModelSaver
3
2
 
4
3
  constructor: ( @element, @options )->
5
4
  @mask = new Lanes.Views.TimedMask( @element, @options.message )
6
5
  @mask.prefixActions( "Save" )
7
6
  _.bindAll(this,'_onError','_onSuccess')
7
+ this.notification = new _.DeferredPromise
8
8
 
9
9
  save: ->
10
10
  @options.model.save({
11
11
  success: this._onSuccess, error: this._onError
12
12
  })
13
13
 
14
- _onSuccess: (rec,resp,options)->
14
+ _onSuccess: (data,success,resp)->
15
15
  @mask.displaySuccess()
16
- this._callback(true,resp)
16
+ this._callback(true,data,resp)
17
17
 
18
- _onError: (rec,resp,options)->
19
- @mask.displayFailure(rec.lastServerMessage)
20
- this._callback(false,resp)
18
+ _onError: (data,success,resp)->
19
+ @mask.displayFailure(@options.model.lastServerMessage)
20
+ this._callback(false,data,resp)
21
21
 
22
- _callback: (success,resp)->
22
+ _callback: (success,data,resp)->
23
23
  @options.callback(success,resp,@options.model) if @options.callback
24
+ this.notification?.resolve(data: data.data, success: success, response: resp)
24
25
 
25
26
 
26
27
  Lanes.Views.SaveNotify = ( view, options={} )->
@@ -28,3 +29,4 @@ Lanes.Views.SaveNotify = ( view, options={} )->
28
29
  _.defaults( options, { model: view.model, message: "Saving, Please Wait…"} )
29
30
  ms = new ModelSaver(el, options)
30
31
  ms.save()
32
+ return ms.notification.promise
@@ -18,7 +18,7 @@ class Lanes.Views.TimedMask
18
18
  color: 'firebrick'
19
19
 
20
20
  failsafeTimeout: 25000
21
- defaultTimeout: 2000
21
+ defaultTimeout: 1500
22
22
 
23
23
  constructor: ( @element, options={} )->
24
24
  _.bindAll(this,'destroy')
data/lanes.gemspec CHANGED
@@ -53,6 +53,6 @@ Gem::Specification.new do |spec|
53
53
  spec.add_dependency "sanitize", "~> 3.0"
54
54
  spec.add_development_dependency "bundler", "~> 1.5"
55
55
  spec.add_development_dependency "growl", "~> 1.0"
56
- spec.add_development_dependency "pry-byebug", "~> 2.0"
57
56
  spec.add_development_dependency "diffy", "~> 3.0"
57
+
58
58
  end
@@ -2,14 +2,20 @@ module Lanes
2
2
  module API
3
3
  class AuthenticationProvider
4
4
 
5
- def initialize(session:nil, params:nil, request_type: type)
6
- @session = session
7
- @params = params
8
- @request_type = request_type
5
+ attr_reader :request
6
+
7
+ def initialize(request)
8
+ @request=request
9
9
  end
10
10
 
11
11
  def current_user
12
- @current_user ||= Lanes::User.where(id: @session['user_id']).first
12
+ @current_user ||= (
13
+ if Lanes.env.test? && request.env['HTTP_X_TESTING_USER']
14
+ Lanes::User.where(login: request.env['HTTP_X_TESTING_USER']).first
15
+ else
16
+ Lanes::User.where(id: @session['user_id']).first
17
+ end
18
+ )
13
19
  end
14
20
 
15
21
  def error_message
@@ -17,7 +23,7 @@ module Lanes
17
23
  end
18
24
 
19
25
  def error_message_for_access
20
- return "Unable to " + case @request_type
26
+ return "Unable to " + case @request.request_method
21
27
  when 'GET' then "read"
22
28
  when 'POST','PATCH','PUT' then "write"
23
29
  when 'DELETE' then "delete"
@@ -28,20 +34,20 @@ module Lanes
28
34
 
29
35
  def allowed_access_to?(klass)
30
36
  return false if current_user.nil?
31
- case @request_type
37
+ case @request.request_method
32
38
  when 'GET'
33
- klass.can_read_attributes?(@params,current_user)
39
+ klass.can_read_attributes?(@request.params,current_user)
34
40
  when 'POST','PATCH','PUT'
35
- klass.can_write_attributes?(@params,current_user)
41
+ klass.can_write_attributes?(@request.params,current_user)
36
42
  when 'DELETE'
37
- klass.can_delete_attributes?(@params,current_user)
43
+ klass.can_delete_attributes?(@request.params,current_user)
38
44
  else
39
45
  false
40
46
  end
41
47
  end
42
48
 
43
49
 
44
- def wrap_request(model, req)
50
+ def wrap_reply(model, req)
45
51
  if allowed_access_to?(model)
46
52
  ::Lanes::User.scoped_to(current_user) do | user |
47
53
  yield
@@ -15,7 +15,7 @@ module Lanes
15
15
 
16
16
  delete "/user-session/:id.json" do
17
17
  session.destroy
18
- wrap_request do
18
+ wrap_reply do
19
19
  { success: true, message: "Logout succeeded", data: {} }
20
20
  end
21
21
  end
@@ -25,9 +25,16 @@ module Lanes
25
25
  def client_paths
26
26
  []
27
27
  end
28
-
28
+ def roles_for_client
29
+ Lanes::Access::Role.all_available.map do |role|
30
+ {
31
+ id: role.to_s.demodulize.underscore,
32
+ name: role.to_s.demodulize
33
+ }
34
+ end
35
+ end
29
36
  def client_bootstrap_data(view)
30
- data = {}
37
+ data = { roles: roles_for_client }
31
38
  if (user_id = view.session['user_id']) && (user = Lanes::User.where( id: user_id ).first)
32
39
  data.merge!(user.workspace_data)
33
40
  end
@@ -41,6 +41,18 @@ module Lanes
41
41
  delete.push( *klasses )
42
42
  end
43
43
 
44
+ def read( *klasses )
45
+ self.read.push( *klasses )
46
+ end
47
+
48
+ def write( *klasses )
49
+ self.write.push( *klasses )
50
+ end
51
+
52
+ def delete( *klasses )
53
+ self.delete.push( *klasses )
54
+ end
55
+
44
56
  def lock(klass, attribute)
45
57
  LockedFields.lock( klass, attribute, self)
46
58
  end
data/lib/lanes/access.rb CHANGED
@@ -6,11 +6,11 @@ module Lanes
6
6
 
7
7
  class << self
8
8
 
9
- def _type_to_ref(klass)
9
+ def type_to_client(klass)
10
10
  klass.to_s.sub(/^(\w+).*?(\w+)$/,'\1.Models.\2')
11
11
  end
12
12
 
13
- def _type_to_id(klass)
13
+ def type_to_client_id(klass)
14
14
  ( klass.is_a?(Class) ? klass : klass.class ).to_s.demodulize.underscore
15
15
  end
16
16
 
@@ -18,18 +18,18 @@ module Lanes
18
18
  {
19
19
  :roles => user.roles.map{ | role |
20
20
  {
21
- type: _type_to_id(role),
22
- read: role.read.map{ |klass| _type_to_ref(klass) },
23
- write: role.write.map{ |klass| _type_to_ref(klass) },
24
- delete: role.delete.map{ |klass| _type_to_ref(klass) }
21
+ type: type_to_client_id(role),
22
+ read: role.read.map{ |klass| type_to_client(klass) },
23
+ write: role.write.map{ |klass| type_to_client(klass) },
24
+ delete: role.delete.map{ |klass| type_to_client(klass) }
25
25
  }
26
26
  },
27
27
  :locked_fields => LockedFields.definitions.map{ | klass, locks |
28
28
  {
29
- type: _type_to_ref(klass),
29
+ type: type_to_client(klass),
30
30
  locks: locks.each_with_object({}) do |(field, grants), result|
31
31
  result[field] = grants.map do |grant|
32
- { role: _type_to_id(grant[:role]), only: grant[:only] }
32
+ { role: type_to_client_id(grant[:role]), only: grant[:only] }
33
33
  end
34
34
  end
35
35
  }
@@ -17,14 +17,14 @@ module Lanes
17
17
  class AuthenticationProvider
18
18
  USER = DummyUser.new
19
19
 
20
- def initialize(session:nil, params:nil, request_type: type)
20
+ def initialize(request)
21
21
  end
22
22
 
23
23
  def current_user
24
24
  USER
25
25
  end
26
26
 
27
- def wrap_request(model, req)
27
+ def wrap_reply(model, req)
28
28
  yield
29
29
  end
30
30
 
@@ -30,17 +30,13 @@ module Lanes
30
30
 
31
31
  def make_handler(model, controller, parent_attribute)
32
32
  lambda do
33
- authentication = Lanes::API::AuthenticationProvider.new(
34
- request_type: request.request_method,
35
- session: session,
36
- params: params
37
- )
38
- authentication.wrap_request(model,self) do
33
+ authentication = Lanes::API::AuthenticationProvider.new(request)
34
+ authentication.wrap_reply(model,self) do
39
35
  if parent_attribute
40
36
  params[:nested_attribute] = Hash[ parent_attribute,
41
37
  params[parent_attribute] ]
42
38
  end
43
- wrap_request(!request.get?) do
39
+ wrap_reply(!request.get?) do
44
40
  yield controller.new(model, authentication, params, data)
45
41
  end
46
42
  end
@@ -52,13 +48,17 @@ module Lanes
52
48
  Lanes.logger.info "UserID: #{session['user_id']}, Params: #{request.params}"
53
49
  end
54
50
 
55
- def wrap_request(with_transaction=true)
51
+ def wrap_reply(with_transaction=true)
56
52
  response = { success: false, message: "No response was generated" }
57
53
  log_request
58
54
  if with_transaction
59
55
  Lanes::Model.transaction do
60
- response = yield
61
- raise ActiveRecord::StatementInvalid if request.env['X_ROLLBACK_AFTER_REQUEST']
56
+ response = yield
57
+ # This is quite possibly a horrible idea.
58
+ # It enables test specs to reset the db state after a request
59
+ if Lanes.env.test? && request.env['HTTP_X_ROLLBACK_AFTER_REQUEST']
60
+ Lanes::Model.connection.rollback_db_transaction
61
+ end
62
62
  end
63
63
  else
64
64
  response = yield
@@ -59,22 +59,31 @@ module Lanes::Concerns
59
59
  "an inverse_of specified."
60
60
  end
61
61
  listeners.each do | name, target |
62
- targets[ name ] = Proc.new{ | record, *args |
63
- record = record.send( association.inverse_of.name )
64
- record.send( target, *( [record] + args ) ) if record
65
- }
62
+ targets[name] = make_association_listener_for(target, association)
66
63
  end
67
64
  begin
68
65
  klass = association.klass # This will throw if the class hasn't been loaded yet
69
- targets.each{ | name, proc | klass._add_event_listener( name, proc ) }
66
+ targets.each{ | name, proc | klass._add_event_listener( name, &proc ) }
70
67
  rescue NameError
71
- pending = PendingEventListeners.all[association.class_name.demodulize]
68
+ pending = Lanes::Concerns::PubSub::PendingListeners.add(association.class_name)
72
69
  targets.each do | name, proc |
73
70
  pending[name] << proc
74
71
  end
75
72
  end
76
73
  end
77
-
74
+ def make_association_listener_for(target, association)
75
+ Proc.new{ | record, *args |
76
+ associated_record = record.public_send(association.inverse_of.name)
77
+ if associated_record
78
+ args.unshift(record)
79
+ if associated_record.is_a? ActiveRecord::Associations::CollectionProxy
80
+ associated_record.each{ |r| r.send(target, *args) }
81
+ else
82
+ associated_record.send(target, *args)
83
+ end
84
+ end
85
+ }
86
+ end
78
87
  def setup_association_export( association, options )
79
88
  export_associations( association.name, options == true ? {} : options )
80
89
  end