lanes 0.0.5 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/Gemfile +0 -1
  4. data/README.md +2 -0
  5. data/client/lanes/data/Bootstrap.coffee +2 -2
  6. data/client/lanes/data/Collection.coffee +4 -0
  7. data/client/lanes/data/Config.coffee +0 -5
  8. data/client/lanes/data/Model.coffee +236 -150
  9. data/client/lanes/data/PubSub.coffee +6 -12
  10. data/client/lanes/data/Sync.coffee +1 -0
  11. data/client/lanes/extension/Extensions.coffee +4 -2
  12. data/client/lanes/lib/MakeBaseClass.coffee +1 -1
  13. data/client/lanes/minimal.js +11 -0
  14. data/client/lanes/minimal.scss.erb +12 -0
  15. data/client/lanes/screens/Base.coffee +1 -2
  16. data/client/lanes/screens/Instance.coffee +52 -0
  17. data/client/lanes/vendor/packaged.js +1 -2
  18. data/client/lanes/views/Base.coffee +12 -10
  19. data/client/lanes/workspace.scss.erb +3 -0
  20. data/client/lanes/workspace/index.js +2 -12
  21. data/docs/command.md +111 -0
  22. data/docs/model.md +188 -0
  23. data/docs/todo-example-part-1.md +71 -0
  24. data/docs/view.md +275 -0
  25. data/{spec/client/jasmine_examples/PlayerSpec.js → docs/welcome.md} +0 -0
  26. data/lanes.gemspec +3 -1
  27. data/lib/lanes/api/helper_methods.rb +8 -0
  28. data/lib/lanes/api/javascript_processor.rb +14 -10
  29. data/lib/lanes/api/pub_sub.rb +7 -7
  30. data/lib/lanes/api/request_wrapper.rb +1 -0
  31. data/lib/lanes/api/root.rb +2 -7
  32. data/lib/lanes/api/sprockets_compressor.rb +6 -2
  33. data/lib/lanes/api/sprockets_extension.rb +25 -9
  34. data/lib/lanes/api/test_specs.rb +13 -9
  35. data/lib/lanes/command.rb +16 -6
  36. data/lib/lanes/command/app.rb +11 -5
  37. data/lib/lanes/command/generate_model.rb +4 -3
  38. data/lib/lanes/command/generate_screen.rb +2 -1
  39. data/lib/lanes/command/generate_view.rb +1 -1
  40. data/lib/lanes/command/named_command.rb +5 -4
  41. data/lib/lanes/command/templates/Gemfile +1 -2
  42. data/lib/lanes/command/templates/client/data/Model.coffee +3 -3
  43. data/lib/lanes/command/templates/client/{namespace-extension.js → index.js} +0 -0
  44. data/lib/lanes/command/templates/client/screens/Screen.coffee +1 -3
  45. data/lib/lanes/command/templates/client/{styles/styles.scss → styles.scss} +0 -0
  46. data/lib/lanes/command/templates/client/views/View.coffee +1 -3
  47. data/lib/lanes/command/templates/config/lanes.rb +1 -1
  48. data/lib/lanes/command/templates/gitignore +1 -0
  49. data/lib/lanes/command/templates/lib/namespace/screen.rb +1 -1
  50. data/lib/lanes/command/templates/public/.gitkeep +0 -0
  51. data/lib/lanes/command/templates/spec/client/Screen.coffee +7 -0
  52. data/lib/lanes/command/templates/spec/client/views/ViewSpec.coffee +2 -2
  53. data/lib/lanes/concerns/all.rb +1 -1
  54. data/lib/lanes/concerns/sanitize_fields.rb +32 -0
  55. data/lib/lanes/concerns/set_attribute_data.rb +4 -4
  56. data/lib/lanes/db.rb +7 -8
  57. data/lib/lanes/extension.rb +37 -3
  58. data/lib/lanes/guard_tasks.rb +2 -2
  59. data/lib/lanes/model.rb +2 -2
  60. data/lib/lanes/screens.rb +1 -0
  61. data/lib/lanes/spec_helper.rb +17 -6
  62. data/{spec → lib/lanes}/testing_models.rb +1 -1
  63. data/lib/lanes/version.rb +1 -1
  64. data/npm-build/compile.coffee +1 -6
  65. data/public/javascripts/jasmine_examples/Player.js +22 -0
  66. data/public/javascripts/jasmine_examples/Song.js +7 -0
  67. data/spec/api/javascript_processor_spec.rb +6 -3
  68. data/spec/concerns/api_path_spec.rb +1 -1
  69. data/spec/concerns/association_extensions_spec.rb +7 -3
  70. data/spec/concerns/attr_accessor_with_default_spec.rb +1 -1
  71. data/spec/concerns/code_identifier_spec.rb +1 -1
  72. data/spec/concerns/export_associations_spec.rb +1 -1
  73. data/spec/concerns/export_methods_spec.rb +1 -14
  74. data/spec/concerns/export_scope_spec.rb +7 -9
  75. data/spec/concerns/exported_limits_spec.rb +1 -1
  76. data/spec/concerns/pub_sub_spec.rb +1 -1
  77. data/spec/concerns/set_attribute_data_spec.rb +16 -24
  78. data/spec/configuration_spec.rb +1 -1
  79. data/spec/helpers/lanes-helpers.coffee +61 -0
  80. data/spec/lanes/data/ModelSpec.coffee +152 -0
  81. data/spec/lanes/data/PubSubSpec.coffee +21 -0
  82. data/spec/{client/view → lanes/views}/BaseSpec.coffee +6 -26
  83. data/spec/numbers_spec.rb +1 -1
  84. data/spec/strings_spec.rb +1 -1
  85. data/views/index.erb +3 -10
  86. data/views/specs.erb +4 -1
  87. metadata +62 -16
  88. data/client/lanes/plugins/trigger.coffee +0 -15
  89. data/client/lanes/workspace/Instance.es6 +0 -64
  90. data/lib/lanes/concerns/sanitize_api_data.rb +0 -15
  91. data/spec/api/user_spec.rb +0 -52
  92. data/spec/fixtures/lanes/users.yml +0 -13
  93. data/spec/locked_fields_spec.rb +0 -27
  94. data/spec/role_collection_spec.rb +0 -19
  95. data/spec/user_role_spec.rb +0 -7
  96. data/spec/user_spec.rb +0 -53
@@ -1,4 +1,4 @@
1
- require_relative '../spec_helper'
1
+ require "lanes/spec_helper"
2
2
 
3
3
 
4
4
  class ExportedLimitsTest < Lanes::TestCase
@@ -1,4 +1,4 @@
1
- require_relative '../spec_helper'
1
+ require "lanes/spec_helper"
2
2
 
3
3
  class PubSubTest < Lanes::TestCase
4
4
 
@@ -1,45 +1,37 @@
1
- require_relative '../spec_helper'
1
+ require "lanes/spec_helper"
2
2
 
3
3
  class SetAttributeDataTest < Lanes::TestCase
4
4
 
5
5
  include TestingModels
6
+ def setup
7
+ @user = DummyUser.new
8
+ end
6
9
 
7
10
  def test_attribute_access
8
11
  tm = TestModel.new
9
- assert tm.setting_attribute_is_allowed?(:name, lanes_users(:admin)), "can't access :name"
10
- assert tm.can_write_attributes?({}, admin), "Can't write to TestModel"
12
+ assert tm.setting_attribute_is_allowed?(:name, @user), "can't access :name"
13
+ assert tm.can_write_attributes?({}, @user), "Can't write to TestModel"
11
14
  data = { name: 'CASH', number: '1200' }
12
- assert_equal( data, TestModel.new.set_attribute_data(data) )
15
+ assert_equal( data, TestModel.new.set_attribute_data(data,@user) )
13
16
  end
14
17
 
15
18
  def test_blacklisting
16
- assert TestModel.new.setting_attribute_is_allowed?(:number,Lanes::User.current)
19
+ assert TestModel.new.setting_attribute_is_allowed?(:number,@user)
17
20
  TestModel.send :blacklist_attributes, :number
18
21
  assert_includes TestModel.blacklisted_attributes, :number
19
- refute TestModel.new.setting_attribute_is_allowed?(:number, Lanes::User.current), "Allowed setting blacklisted attribute"
20
- record = TestModel.from_attribute_data({ name: 'CASH', number: '1234'})
22
+ refute TestModel.new.setting_attribute_is_allowed?(:number, @user), "Allowed setting blacklisted attribute"
23
+ record = TestModel.from_attribute_data({ name: 'CASH', number: '1234'}, @user)
21
24
  assert_nil record.number
22
25
  end
23
26
 
24
27
  def test_whitelisting
25
- refute TestModel.new.setting_attribute_is_allowed?('updated_at',Lanes::User.current)
28
+ refute TestModel.new.setting_attribute_is_allowed?('updated_at', @user)
26
29
  TestModel.send :whitelist_attributes, :updated_at
27
- assert TestModel.new.setting_attribute_is_allowed?('updated_at',Lanes::User.current)
28
- end
29
-
30
- def test_recursive_cleaning_belongs_to
31
- assert TestModel.has_exported_association?( :bt, admin )
32
- data = { name: 'testing', bt: { description: 'childish', secret_field: 'dr evil' } }
33
- record = TestModel.new
34
- cleaned = record.set_attribute_data(data)
35
- assert_equal( { name: 'testing', bt:{ description: 'childish' } }, cleaned)
36
- assert record.bt
37
- assert_equal 'childish', record.bt.description
38
- assert_nil record.bt.secret_field
30
+ assert TestModel.new.setting_attribute_is_allowed?('updated_at', @user)
39
31
  end
40
32
 
41
33
  def test_recursive_cleaning_has_many
42
- assert TestModel.has_exported_association?( :hm, admin )
34
+ assert TestModel.has_exported_association?( :hm, @user )
43
35
  data = {
44
36
  name: 'testing',
45
37
  hm: [
@@ -47,7 +39,7 @@ class SetAttributeDataTest < Lanes::TestCase
47
39
  { description: 'childish-2', secret_field: 'dr evil' }
48
40
  ]}
49
41
  record = TestModel.new
50
- cleaned_data = record.set_attribute_data(data)
42
+ cleaned_data = record.set_attribute_data(data,@user)
51
43
  assert_equal 2, record.hm.length
52
44
  assert_equal cleaned_data, {:name=>"testing", :hm=>[{:description=>"childish-1"}, {:description=>"childish-2"}]}
53
45
  assert_saves record
@@ -56,10 +48,10 @@ class SetAttributeDataTest < Lanes::TestCase
56
48
  def test_sending_to_method
57
49
  tm = TestModel.new
58
50
  def tm.meaning_of_life=(var); @setting = var end
59
- tm.set_attribute_data({ meaning_of_life: 42 })
51
+ tm.set_attribute_data({ meaning_of_life: 42 }, @user)
60
52
  assert_nil tm.instance_variable_get(:@setting)
61
53
  TestModel.send :whitelist_attributes, :meaning_of_life
62
- tm.set_attribute_data({ meaning_of_life: 42 })
54
+ tm.set_attribute_data({ meaning_of_life: 42 }, @user)
63
55
  assert_equal 42, tm.instance_variable_get(:@setting)
64
56
  end
65
57
 
@@ -1,4 +1,4 @@
1
- require_relative 'spec_helper'
1
+ require "lanes/spec_helper"
2
2
 
3
3
  describe Lanes::Configuration do
4
4
 
@@ -0,0 +1,61 @@
1
+ Lanes.namespace 'Testing'
2
+
3
+ class Lanes.Testing.View extends Lanes.Views.Base
4
+ template: "<p>hi</p"
5
+ events: { "click #mylink": 'onClick' }
6
+ onClick: Lanes.emptyFn
7
+
8
+ Lanes.Vendor.MessageBus = {
9
+ subscribe: jasmine.createSpy('subscribe')
10
+ unsubscribe: jasmine.createSpy('unsubscribe')
11
+ start: Lanes.emptyFn
12
+ }
13
+
14
+ class Lanes.Testing.Model extends Lanes.Data.Model
15
+ api_path: "test"
16
+ props:
17
+ id: 'number',
18
+ name: ['string', true],
19
+ html: 'string',
20
+ url: 'string',
21
+ something: 'string',
22
+ fireDanger: 'string'
23
+
24
+ session:
25
+ active: 'boolean'
26
+
27
+ derived:
28
+ classes:
29
+ deps: ['something', 'fireDanger', 'active'],
30
+ fn: -> this.something + this.active;
31
+
32
+ class Lanes.Testing.Collection extends Lanes.Data.Collection
33
+ model: Lanes.Testing.Model
34
+
35
+ syncResponse = (method,model,options)->
36
+ if options.success && syncReply.success
37
+ options.success(model, syncReply, {})
38
+ if options.failure && !syncReply.success
39
+ options.failure(model, syncReply, {})
40
+
41
+ syncReply = {}
42
+
43
+ window.syncSucceedWith = (data)->
44
+ syncReply.success = true
45
+ syncReply.data = data
46
+
47
+ beforeEach ->
48
+
49
+ syncReply = {
50
+ success: true
51
+ message: ''
52
+ data: {}
53
+ }
54
+
55
+ Lanes.Testing.syncSpy = jasmine.createSpy('sync').and.callFake(syncResponse)
56
+ Lanes.Testing.syncSpy.lastOptions = ->
57
+ this.calls.mostRecent().args[2]
58
+ Lanes.Data.Model::sync = Lanes.Testing.syncSpy
59
+ Lanes.Data.Collection::sync = Lanes.Testing.syncSpy
60
+
61
+
@@ -0,0 +1,152 @@
1
+
2
+ describe "Model Suite", ->
3
+ Model = undefined
4
+ Collection = undefined
5
+ class Color
6
+ constructor: -> super
7
+ props: { id: 'integer', rgb: 'string' }
8
+ Lanes.Data.Model.extend(Color)
9
+
10
+ makeModel = (props,args={})->
11
+ _.extend(Model.prototype, props)
12
+ Lanes.Data.Model.extend(Model)
13
+ Collection::model = Model
14
+ Lanes.Data.Collection.extend(Collection)
15
+ collection = new Collection
16
+ return collection.add(new Model(args))
17
+
18
+ beforeEach ->
19
+ class TestModel
20
+ constructor: -> super
21
+ Model = TestModel
22
+ class TestCollection
23
+ constructor: -> super
24
+ Collection = TestCollection
25
+
26
+
27
+ it "tracks unsaved attributes", ->
28
+ model = makeModel({
29
+ session:
30
+ foo: 'string'
31
+ bar: 'integer'
32
+ props:
33
+ saved: 'string'
34
+ }, {bar: 'baz'})
35
+ expect(model.isDirty()).toBeFalse()
36
+ model.foo = 'baz' # session prop
37
+ expect(model.isDirty()).toBeFalse()
38
+ expect(model.unsavedData()).toBeEmptyObject()
39
+ model.set( saved: 'true' )
40
+ expect(model.isDirty()).toBeTrue()
41
+
42
+
43
+ it "can tell if it has attributes", ->
44
+ model = makeModel({
45
+ session:
46
+ foo: 'string'
47
+ props:
48
+ bar: 'string'
49
+ })
50
+ expect(model.hasAttribute('missing')).toBeFalse()
51
+ expect(model.hasAttribute('foo')).toBeTrue()
52
+ expect(model.hasAttribute('bar')).toBeTrue()
53
+
54
+ it "provides data for saving", ->
55
+ # model with other types of data, but should only save "props"
56
+ model = makeModel({
57
+ associations:
58
+ color:{ model: Color }
59
+ session:
60
+ unsaved: 'string'
61
+ props:
62
+ id: 'integer'
63
+ foo: 'string'
64
+ bar: 'string'
65
+ derived:
66
+ atest:
67
+ fn: -> 'blarg'
68
+
69
+ })
70
+ model.set( id: 10, unsaved: 'falsify', color: { rgb: '99FFFF' } )
71
+
72
+ expect( model.dataForSave() ).toEqual( id: 10, color: { rgb: '99FFFF' } )
73
+ model.foo = 'a value'
74
+ a=model.changeMonitor.changedAttributes()
75
+ expect( model.dataForSave() ).toEqual( id: 10, foo: 'a value', color: { rgb: '99FFFF' } )
76
+
77
+ it "can be saved", ->
78
+ model = makeModel({
79
+ props:
80
+ id: 'integer'
81
+ foo: 'string'
82
+ bar: 'string'
83
+
84
+ }, { foo: 'one, two, three'} )
85
+ expect(model.isDirty()).toBeTrue()
86
+ expect(model.unsavedData()).toEqual( foo: 'one, two, three' )
87
+ model.save()
88
+ expect(model.sync).toHaveBeenCalledWith('create', model, jasmine.any(Object))
89
+ model.id=11
90
+ expect(model.isNew()).toBeFalse()
91
+ model.sync.calls.reset()
92
+ syncSucceedWith({
93
+ foo: 'a new foo value'
94
+ })
95
+ model.save()
96
+ expect(model.sync).toHaveBeenCalledWith('patch', model, jasmine.any(Object))
97
+ expect(model.foo).toEqual('a new foo value')
98
+
99
+ it "can be fetched",->
100
+ model = makeModel({
101
+ props:
102
+ { id: 'integer', foo: 'string' }
103
+ })
104
+ syncSucceedWith({
105
+ foo: 'foo value'
106
+ })
107
+ model.fetch()
108
+ expect(model.sync).toHaveBeenCalledWith('read', model, jasmine.any(Object))
109
+ expect(model.foo).toEqual('foo value')
110
+ options = model.sync.lastOptions()
111
+ expect(options).toHaveTrue('ignoreUnsaved')
112
+ expect(options).toHaveNumberWithinRange('limit',1,1)
113
+
114
+
115
+ it "can be destroyed", ->
116
+ model = makeModel({
117
+ props: { id: 'integer' }
118
+ },{ id: 1 })
119
+ collection = model.collection
120
+ expect(collection.length).toEqual(1)
121
+ model.destroy()
122
+ expect(model.sync)
123
+ .toHaveBeenCalledWith('delete', model, jasmine.any(Object))
124
+ expect(collection.length).toEqual(0)
125
+
126
+ it "sets associations", ->
127
+ model = makeModel({
128
+ associations:
129
+ color:{ model: Color }
130
+ props: { id: 'integer', foo: 'string' }
131
+ },{ id: 1 })
132
+ expect(model.color).toBeObject()
133
+ model.set(color: { rgb: '99FFFF' })
134
+ expect(model.color.rgb).toEqual('99FFFF')
135
+ model.setFromResponse(color:{ rgb: 'FFA500' })
136
+ expect(model.color.rgb).toEqual('FFA500')
137
+
138
+ it "can fetch associations", ->
139
+ model = makeModel({
140
+ associations:
141
+ color:{ model: Color }
142
+ props: { id: 'integer', foo: 'string' }
143
+ },{ id: 1 })
144
+ model.withAssociations('color')
145
+ expect(model.sync).toHaveBeenCalledWith('read', model, jasmine.any(Object))
146
+ options = model.sync.lastOptions()
147
+ expect(options.include).toEqual(['color'])
148
+
149
+ it "assigns an inverse collection property", ->
150
+ model = makeModel({})
151
+ expect(Model::Collection).toEqual(Collection)
152
+
@@ -0,0 +1,21 @@
1
+
2
+ describe "PubSub Suite", ->
3
+
4
+ it "checks in/out a model when it binds to a view", ->
5
+ view = new Lanes.Testing.View
6
+ model = new Lanes.Testing.Model(id:1)
7
+ spyOn(Lanes.Data.PubSub,'add').and.callThrough()
8
+ spyOn(Lanes.Data.PubSub,'remove')
9
+ view.model = model
10
+ expect(Lanes.Data.PubSub.add).toHaveBeenCalledWith(model)
11
+ expect(Lanes.Vendor.MessageBus.subscribe)
12
+ .toHaveBeenCalledWith("/test/1", jasmine.any(Function))
13
+ view.unset('model')
14
+ expect(Lanes.Data.PubSub.remove).toHaveBeenCalledWith(model)
15
+
16
+ it "can retrieve a model after checkin", ->
17
+ model = new Lanes.Testing.Model(id: 11, name:'bar')
18
+ Lanes.Data.PubSub.add(model)
19
+ checkin = Lanes.Data.PubSub.instanceFor(Lanes.Testing.Model, 11)
20
+ expect(checkin).toEqual(model)
21
+
@@ -1,23 +1,4 @@
1
1
 
2
- class Model extends Lanes.Data.Model
3
- props:
4
- id: 'number',
5
- name: ['string', true],
6
- html: 'string',
7
- url: 'string',
8
- something: 'string',
9
- fireDanger: 'string'
10
-
11
- session:
12
- active: 'boolean'
13
-
14
- derived:
15
- classes:
16
- deps: ['something', 'fireDanger', 'active'],
17
- fn: -> this.something + this.active;
18
-
19
- class Collection extends Lanes.Data.Collection
20
- model: Model
21
2
 
22
3
  describe "BaseView Suite", ->
23
4
  View = undefined
@@ -40,7 +21,6 @@ describe "BaseView Suite", ->
40
21
  view.render()
41
22
  expect(spy).toHaveBeenCalled()
42
23
 
43
-
44
24
  it "renders a template string", ->
45
25
  view = makeView({
46
26
  template: "<p>hi</p"
@@ -64,7 +44,7 @@ describe "BaseView Suite", ->
64
44
  expect(spy).toHaveBeenCalledWith(jasmine.any(Object),22)
65
45
 
66
46
  it "updates from bindings", ->
67
- model = new Model({ name: "Bob" })
47
+ model = new Lanes.Testing.Model({ name: "Bob" })
68
48
  view = makeView({
69
49
  bindings: {
70
50
  "model.name": { hook: 'link' }
@@ -85,7 +65,7 @@ describe "BaseView Suite", ->
85
65
  constructor: -> super
86
66
  session: { answer: 'string' }
87
67
  Lanes.Views.Base.extend(LowerView)
88
- model = new Model
68
+ model = new Lanes.Testing.Model
89
69
  view = makeView({
90
70
  subviews:
91
71
  lower:
@@ -108,7 +88,7 @@ describe "BaseView Suite", ->
108
88
  constructor: -> super
109
89
  bindings: { "model.name": { type: "text" } }
110
90
  Lanes.Views.Base.extend(LowerView)
111
- collection = new Collection
91
+ collection = new Lanes.Testing.Collection
112
92
  view = makeView({
113
93
  subviews:
114
94
  lower:
@@ -141,7 +121,7 @@ describe "BaseView Suite", ->
141
121
  it "invokes model events", ->
142
122
  nameSpy = jasmine.createSpy()
143
123
  urlSpy = jasmine.createSpy()
144
- model = new Model
124
+ model = new Lanes.Testing.Model
145
125
  view = makeView({
146
126
  modelEvents:
147
127
  'change:name': nameSpy
@@ -159,8 +139,8 @@ describe "BaseView Suite", ->
159
139
  collectionEvents:
160
140
  all: 'onEvent'
161
141
  onEvent: eventSpy
162
- initialize: -> this.collection = new Collection
142
+ initialize: -> this.collection = new Lanes.Testing.Collection
163
143
  })
164
144
  expect(eventSpy).not.toHaveBeenCalled()
165
- view.collection.add(new Model)
145
+ view.collection.add(new Lanes.Testing.Model)
166
146
  expect(eventSpy).toHaveBeenCalled()
data/spec/numbers_spec.rb CHANGED
@@ -1,4 +1,4 @@
1
- require_relative 'spec_helper'
1
+ require "lanes/spec_helper"
2
2
 
3
3
  describe Lanes::Numbers do
4
4
 
data/spec/strings_spec.rb CHANGED
@@ -1,4 +1,4 @@
1
- require_relative 'spec_helper'
1
+ require "lanes/spec_helper"
2
2
 
3
3
  describe Lanes::Strings do
4
4
 
data/views/index.erb CHANGED
@@ -5,21 +5,14 @@
5
5
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui"/>
7
7
  <title><%= Lanes.config.app_title %></title>
8
- <%= javascript_tag 'lanes/workspace' %>
9
- <%= stylesheet_tag 'lanes/workspace' %>
8
+ <%= lanes_javascript_tag %>
9
+ <%= lanes_stylesheet_tag %>
10
10
  <script type="text/javascript">
11
- Lanes.Workspace.create('.lanes-root', {
12
- csrf_token: "<%= csrf_token %>",
13
- view: "<%= Lanes.config.root_view %>",
14
- root_path: "<%= lanes_api_url %>"
15
- }, <%= client_bootstrap_data %> );
11
+ Lanes.renderScreenTo('body', <%= client_bootstrap_data %> );
16
12
  </script>
17
13
  </head>
18
14
 
19
15
  <body>
20
16
 
21
- <div class="lanes-root">
22
- </div>
23
-
24
17
  </body>
25
18
  </html>