lanes 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
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