volt 0.9.3.pre3 → 0.9.3.pre4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -2
  3. data/app/volt/models/user.rb +5 -0
  4. data/app/volt/tasks/store_tasks.rb +2 -2
  5. data/app/volt/tasks/user_tasks.rb +1 -1
  6. data/lib/volt/cli/asset_compile.rb +0 -2
  7. data/lib/volt/cli/generate.rb +8 -3
  8. data/lib/volt/cli.rb +3 -9
  9. data/lib/volt/controllers/actions.rb +6 -1
  10. data/lib/volt/controllers/http_controller.rb +16 -7
  11. data/lib/volt/controllers/model_controller.rb +21 -0
  12. data/lib/volt/models/array_model.rb +26 -3
  13. data/lib/volt/models/model.rb +18 -19
  14. data/lib/volt/models/persistors/array_store.rb +2 -10
  15. data/lib/volt/models/root_models/store_root.rb +17 -4
  16. data/lib/volt/models/validations/validations.rb +1 -1
  17. data/lib/volt/models/validators/unique_validator.rb +1 -1
  18. data/lib/volt/models.rb +1 -1
  19. data/lib/volt/page/bindings/attribute_binding.rb +5 -4
  20. data/lib/volt/page/bindings/base_binding.rb +17 -0
  21. data/lib/volt/page/bindings/content_binding.rb +7 -5
  22. data/lib/volt/page/bindings/each_binding.rb +62 -51
  23. data/lib/volt/page/bindings/event_binding.rb +14 -0
  24. data/lib/volt/page/bindings/view_binding.rb +1 -1
  25. data/lib/volt/reactive/computation.rb +22 -13
  26. data/lib/volt/reactive/dependency.rb +0 -24
  27. data/lib/volt/router/routes.rb +35 -0
  28. data/lib/volt/server/forking_server.rb +26 -3
  29. data/lib/volt/server/message_bus/peer_to_peer/peer_connection.rb +1 -1
  30. data/lib/volt/server/message_bus/peer_to_peer/server_tracker.rb +1 -1
  31. data/lib/volt/server/message_bus/peer_to_peer.rb +28 -21
  32. data/lib/volt/server/middleware/default_middleware_stack.rb +67 -0
  33. data/lib/volt/server/middleware/middleware_stack.rb +58 -0
  34. data/lib/volt/server/rack/http_request.rb +1 -1
  35. data/lib/volt/server/rack/http_resource.rb +7 -0
  36. data/lib/volt/server/rack/keep_alive.rb +20 -0
  37. data/lib/volt/server/socket_connection_handler.rb +10 -1
  38. data/lib/volt/server.rb +6 -76
  39. data/lib/volt/utils/promise_extensions.rb +5 -1
  40. data/lib/volt/utils/set_patch.rb +25 -0
  41. data/lib/volt/utils/timers.rb +12 -0
  42. data/lib/volt/version.rb +1 -1
  43. data/lib/volt/volt/app.rb +13 -1
  44. data/lib/volt/volt/server_setup/app.rb +19 -1
  45. data/lib/volt/volt/users.rb +11 -22
  46. data/lib/volt.rb +1 -0
  47. data/spec/apps/kitchen_sink/Gemfile +1 -1
  48. data/spec/apps/kitchen_sink/app/main/config/routes.rb +1 -1
  49. data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +22 -0
  50. data/spec/apps/kitchen_sink/app/main/views/main/bindings.html +10 -0
  51. data/spec/apps/kitchen_sink/app/main/views/main/store_demo.html +9 -0
  52. data/spec/controllers/http_controller_spec.rb +27 -0
  53. data/spec/integration/bindings_spec.rb +29 -0
  54. data/spec/integration/store_spec.rb +7 -7
  55. data/spec/models/associations_spec.rb +1 -1
  56. data/spec/models/model_spec.rb +10 -0
  57. data/spec/models/permissions_spec.rb +7 -4
  58. data/spec/reactive/computation_spec.rb +33 -5
  59. data/spec/router/routes_spec.rb +69 -0
  60. data/spec/server/middleware/middleware_handler.rb +24 -0
  61. data/spec/spec_helper.rb +1 -1
  62. data/spec/tasks/user_tasks_spec.rb +3 -2
  63. data/templates/project/Gemfile.tt +2 -2
  64. data/templates/project/config/base/index.html +5 -1
  65. metadata +10 -5
  66. data/spec/apps/kitchen_sink/app/main/views/main/store.html +0 -9
  67. data/templates/project/app/main/models/.empty_directory +0 -0
@@ -38,7 +38,16 @@ module Volt
38
38
  # Messages are json and wrapped in an array
39
39
  message = JSON.parse(message).first
40
40
 
41
- @@dispatcher.dispatch(self, message)
41
+ begin
42
+ @@dispatcher.dispatch(self, message)
43
+ rescue => e
44
+ if defined?(DRb::DRbConnError) && e.is_a?(DRb::DRbConnError)
45
+ # The child process was restarting, so drb failed to send
46
+ else
47
+ # re-raise the issue
48
+ raise
49
+ end
50
+ end
42
51
  end
43
52
 
44
53
  def send_message(*args)
data/lib/volt/server.rb CHANGED
@@ -7,43 +7,17 @@ require 'sass'
7
7
  require 'volt/utils/tilt_patch'
8
8
  require 'sprockets-sass'
9
9
 
10
-
11
10
  require 'volt'
12
11
  require 'volt/tasks/dispatcher'
13
12
  require 'volt/tasks/task'
14
13
  require 'volt/server/component_handler'
15
14
  require 'volt/server/rack/component_paths'
16
- require 'volt/server/rack/index_files'
17
- require 'volt/server/rack/http_resource'
18
- require 'volt/server/rack/opal_files'
19
- require 'volt/server/rack/quiet_common_logger'
20
15
  require 'volt/page/page'
21
16
 
22
- require 'volt/volt/core'
23
17
  require 'volt/server/websocket/websocket_handler'
24
18
  require 'volt/utils/read_write_lock'
25
19
  require 'volt/server/forking_server'
26
20
 
27
- module Rack
28
- # TODO: For some reason in Rack (or maybe thin), 304 headers close
29
- # the http connection. We might need to make this check if keep
30
- # alive was in the request.
31
- class KeepAlive
32
- def initialize(app)
33
- @app = app
34
- end
35
-
36
- def call(env)
37
- status, headers, body = @app.call(env)
38
-
39
- if status == 304 && env['HTTP_CONNECTION'] && env['HTTP_CONNECTION'].downcase == 'keep-alive'
40
- headers['Connection'] = 'keep-alive'
41
- end
42
-
43
- [status, headers, body]
44
- end
45
- end
46
- end
47
21
 
48
22
  module Volt
49
23
  class Server
@@ -54,7 +28,7 @@ module Volt
54
28
  @root_path = root_path || Dir.pwd
55
29
  @volt_app = app
56
30
 
57
- @app_path = File.expand_path(File.join(@root_path, 'app'))
31
+ @app_path = File.expand_path(File.join(@root_path, 'app'))
58
32
 
59
33
  display_welcome
60
34
  end
@@ -70,7 +44,10 @@ module Volt
70
44
  @volt_app ||= Volt.boot(@root_path)
71
45
  end
72
46
 
73
- # App returns the main rack app. In development it will fork a
47
+ # App returns the main rack app. In development it will use ForkingServer,
48
+ # which forks the app and processes responses in a child process, that is
49
+ # killed when code changes and reforked. (This provides simple fast code
50
+ # reloading)
74
51
  def app
75
52
  app = Rack::Builder.new
76
53
 
@@ -94,7 +71,7 @@ module Volt
94
71
 
95
72
  # Setup the dispatcher (it stays this class during its run)
96
73
  SocketConnectionHandler.dispatcher = Dispatcher.new(@volt_app)
97
- app.run(new_server)
74
+ app.run(@volt_app.middleware)
98
75
  else
99
76
  # In developer
100
77
  app.run ForkingServer.new(self)
@@ -102,52 +79,5 @@ module Volt
102
79
 
103
80
  app
104
81
  end
105
-
106
- # new_server returns the core of the Rack app.
107
- # Volt.boot should be called before generating the new server
108
- def new_server
109
- @rack_app = Rack::Builder.new
110
-
111
- # Should only be used in production
112
- if Volt.config.deflate
113
- @rack_app.use Rack::Deflater
114
- @rack_app.use Rack::Chunked
115
- end
116
-
117
- @rack_app.use Rack::ContentLength
118
-
119
- @rack_app.use Rack::KeepAlive
120
- @rack_app.use Rack::ConditionalGet
121
- @rack_app.use Rack::ETag
122
-
123
- @rack_app.use QuietCommonLogger
124
- @rack_app.use Rack::ShowExceptions
125
-
126
- component_paths = @volt_app.component_paths
127
- @rack_app.map '/components' do
128
- run ComponentHandler.new(component_paths)
129
- end
130
-
131
- # Serve the opal files
132
- opal_files = OpalFiles.new(@rack_app, @app_path, @volt_app.component_paths)
133
-
134
- # Serve the main html files from public, also figure out
135
- # which JS/CSS files to serve.
136
- @rack_app.use IndexFiles, @volt_app, @volt_app.component_paths, opal_files
137
-
138
- @rack_app.use HttpResource, @volt_app, @volt_app.router
139
-
140
- @rack_app.use Rack::Static,
141
- urls: ['/'],
142
- root: 'config/base',
143
- index: '',
144
- header_rules: [
145
- [:all, { 'Cache-Control' => 'public, max-age=86400' }]
146
- ]
147
-
148
- @rack_app.run lambda { |env| [404, { 'Content-Type' => 'text/html; charset=utf-8' }, ['404 - page not found']] }
149
-
150
- @rack_app
151
- end
152
82
  end
153
83
  end
@@ -12,6 +12,10 @@ class Promise
12
12
  promise
13
13
  end
14
14
 
15
+ def respond_to_missing(method_name, include_private = false)
16
+ true
17
+ end
18
+
15
19
  # Allow .each to be called directly on promises
16
20
  def each(&block)
17
21
  raise ArgumentError, 'no block given' unless block
@@ -69,7 +73,7 @@ class Promise
69
73
 
70
74
  if error
71
75
  err_str = "Exception in Promise at .sync: #{error.inspect}"
72
- err_str += error.backtrace.join("\n")
76
+ err_str += error.backtrace.join("\n") if error.respond_to?(:backtrace)
73
77
  Volt.logger.error(err_str)
74
78
  fail error
75
79
  else
@@ -0,0 +1,25 @@
1
+ # Temp until https://github.com/opal/opal/pull/596
2
+ require 'set'
3
+
4
+ class Set
5
+ def delete(o)
6
+ if include?(o)
7
+ @hash.delete(o)
8
+ true
9
+ else
10
+ nil
11
+ end
12
+ end
13
+
14
+ def delete_if
15
+ block_given? or return enum_for(__method__)
16
+ # @hash.delete_if should be faster, but using it breaks the order
17
+ # of enumeration in subclasses.
18
+ select { |o| yield o }.each { |o| @hash.delete(o) }
19
+ self
20
+ end
21
+
22
+ def to_a
23
+ @hash.keys
24
+ end
25
+ end
@@ -14,6 +14,18 @@ module Volt
14
14
  end
15
15
  end
16
16
 
17
+ # yields the passed in block after interval ms, or immediately if on the
18
+ # server.
19
+ def self.client_set_timeout(interval)
20
+ if Volt.in_browser?
21
+ `setTimeout(function() {`
22
+ yield
23
+ `}, interval)`
24
+ else
25
+ yield
26
+ end
27
+ end
28
+
17
29
  # On the server, we need to manually flush next tick timers.
18
30
  # This is done automatically in the console after each enter.
19
31
  def self.flush_next_tick_timers!
data/lib/volt/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Volt
2
2
  module Version
3
- STRING = '0.9.3.pre3'
3
+ STRING = '0.9.3.pre4'
4
4
  end
5
5
  end
data/lib/volt/volt/app.rb CHANGED
@@ -37,7 +37,8 @@ module Volt
37
37
  end
38
38
 
39
39
  attr_reader :component_paths, :router, :page, :live_query_pool,
40
- :channel_live_queries, :app_path, :database, :message_bus
40
+ :channel_live_queries, :app_path, :database, :message_bus,
41
+ :middleware
41
42
 
42
43
  def initialize(app_path=nil)
43
44
  if Volt.server? && !app_path
@@ -53,6 +54,14 @@ module Volt
53
54
  setup_page
54
55
 
55
56
  if RUBY_PLATFORM != 'opal'
57
+ # We need to run the root config first so we can setup the Rack::Session
58
+ # middleware.
59
+ run_config
60
+
61
+ # Setup all of the middleware we can before we load the users components
62
+ # since the users components might want to add middleware during boot.
63
+ setup_preboot_middleware
64
+
56
65
  # Setup all app paths
57
66
  setup_paths
58
67
 
@@ -69,6 +78,9 @@ module Volt
69
78
 
70
79
  reset_query_pool!
71
80
 
81
+ # Setup the middleware that we can only setup after all components boot.
82
+ setup_postboot_middleware
83
+
72
84
  start_message_bus
73
85
  end
74
86
  end
@@ -1,6 +1,10 @@
1
1
  # The following setup handles setting up the app on the server.
2
2
  unless RUBY_PLATFORM == 'opal'
3
3
  require 'volt/server/message_bus/peer_to_peer'
4
+ require 'volt/server/middleware/middleware_stack'
5
+ require 'volt/server/middleware/default_middleware_stack'
6
+ require 'volt/volt/core'
7
+
4
8
  end
5
9
 
6
10
  module Volt
@@ -21,6 +25,16 @@ module Volt
21
25
  @router = Routes.new
22
26
  end
23
27
 
28
+
29
+ def setup_preboot_middleware
30
+ @middleware = MiddlewareStack.new
31
+ DefaultMiddlewareStack.preboot_setup(self, @middleware)
32
+ end
33
+
34
+ def setup_postboot_middleware
35
+ DefaultMiddlewareStack.postboot_setup(self, @middleware)
36
+ end
37
+
24
38
  def require_http_controllers
25
39
  @component_paths.app_folders do |app_folder|
26
40
  # Sort so we get consistent load order across platforms
@@ -32,11 +46,15 @@ module Volt
32
46
  end
33
47
  end
34
48
 
49
+ # This config needs to run earlier than others
50
+ def run_config
51
+ require("#{Volt.root}/config/app.rb")
52
+ end
35
53
 
36
54
  # Load in all .rb files in the initializers folders and the config/app.rb
37
55
  # file.
38
56
  def run_app_and_initializers
39
- files = ["#{Volt.root}/config/app.rb"]
57
+ files = []
40
58
 
41
59
  # Include the root initializers
42
60
  files += Dir[Volt.root + '/config/initializers/*.rb']
@@ -53,13 +53,19 @@ module Volt
53
53
 
54
54
  # True if the user is logged in and the user is loaded
55
55
  def current_user?
56
- !!current_user
56
+ current_user.then do |user|
57
+ !!user
58
+ end
57
59
  end
58
60
 
59
61
  # Return the current user.
60
62
  def current_user
61
- # Run first on the query, or return nil
62
- user_query.try(:first)
63
+ user_id = current_user_id
64
+ if user_id
65
+ $page.store._users.where(id: user_id).first
66
+ else
67
+ Promise.new.resolve(nil)
68
+ end
63
69
  end
64
70
 
65
71
  # Put in a deprecation placeholder
@@ -69,13 +75,8 @@ module Volt
69
75
  end
70
76
 
71
77
  def fetch_current_user
72
- u_query = user_query
73
- if u_query
74
- u_query.fetch_first
75
- else
76
- # No user, resolve nil
77
- Promise.new.resolve(nil)
78
- end
78
+ Volt.logger.warn("Deprication Warning: fetch current user have been depricated, Volt.current_user returns a promise now.")
79
+ current_user
79
80
  end
80
81
 
81
82
  # Login the user, return a promise for success
@@ -110,17 +111,5 @@ module Volt
110
111
 
111
112
  user_id_signature
112
113
  end
113
-
114
- private
115
-
116
- # Returns a query for the current user_id or nil if there is no user_id
117
- def user_query
118
- user_id = current_user_id
119
- if user_id
120
- $page.store._users.where(_id: user_id)
121
- else
122
- nil
123
- end
124
- end
125
114
  end
126
115
  end
data/lib/volt.rb CHANGED
@@ -5,6 +5,7 @@ require 'volt/reactive/dependency'
5
5
  require 'volt/utils/modes'
6
6
  require 'volt/utils/volt_user_error'
7
7
  require 'volt/utils/boolean_patch'
8
+ require 'volt/utils/set_patch'
8
9
 
9
10
  require 'volt/config'
10
11
  require 'volt/data_stores/data_store' unless RUBY_PLATFORM == 'opal'
@@ -14,7 +14,7 @@ gem 'volt-fields'
14
14
  gem 'volt-user_templates'
15
15
 
16
16
  # use mongo for data store while testing
17
- gem 'volt-mongo', path: '/Users/ryanstout/Sites/volt/apps/volt-mongo'
17
+ gem 'volt-mongo'
18
18
 
19
19
  gem 'opal'
20
20
 
@@ -3,7 +3,7 @@
3
3
  client '/bindings/{{ route_test }}', action: 'bindings'
4
4
  client '/bindings', action: 'bindings'
5
5
  client '/form', action: 'form'
6
- client '/store', action: 'store'
6
+ client '/store', action: 'store_demo'
7
7
  client '/cookie_test', action: 'cookie_test'
8
8
  client '/flash', action: 'flash'
9
9
  client '/yield', action: 'yield'
@@ -10,6 +10,10 @@ module Main
10
10
  a[{}] = 5
11
11
  end
12
12
 
13
+ def store_demo
14
+ puts "STORE DEMO"
15
+ end
16
+
13
17
  def form_ready
14
18
  `$('#title').html('form_ready')`
15
19
  `$('select#location').val('AL').change()` # have to trigger manually as this is not user initiaized action
@@ -54,6 +58,24 @@ module Main
54
58
  page._show = value
55
59
  end
56
60
 
61
+ def show_with_delay
62
+ changes = Promise.new
63
+ page._items = changes
64
+ `
65
+ setTimeout(function () {
66
+ #{changes.resolve(0.upto(100).to_a)}
67
+ }, 50);
68
+ `
69
+ page._items = 901.upto(1000).to_a
70
+ end
71
+
72
+ def show_without_delay
73
+ changes = Promise.new
74
+ changes.resolve(1.upto(200).to_a)
75
+ page._items = 901.upto(1000).to_a
76
+ page._items = changes
77
+ end
78
+
57
79
  private
58
80
 
59
81
  # the main template contains a #template binding that shows another
@@ -134,6 +134,16 @@
134
134
  <a id='showtrue' e-click='set_show(true)'>set _show true</a>
135
135
  <a id='showfalse' e-click='set_show(false)'>set _show false</a>
136
136
 
137
+ <h2>Each Bindings</h2>
138
+ <ul id='eachbinding'>
139
+ {{ _items.each do |item| }}
140
+ <li>{{ item }}</li>
141
+ {{ end }}
142
+ </ul>
143
+
144
+ <a id='each-show-with-delay' e-click='show_with_delay'>Show with Delay</a>
145
+ <a id='each-show-without-delay' e-click='show_without_delay'>Show without Delay</a>
146
+
137
147
  <h2>Content</h2>
138
148
 
139
149
  <p id="escapeContent">{{{this is {{escaped}}}}}</p>
@@ -0,0 +1,9 @@
1
+ <:Title>
2
+ Store
3
+
4
+ <:Body>
5
+ <h1>Store</h1>
6
+
7
+ <p>Sync between nested root properties.</p>
8
+ <input id="field1" value="{{ store._name }}" /><br />
9
+ <!-- <input id="field2" value="{{ store._name }}" /> -->
@@ -7,7 +7,12 @@ if RUBY_PLATFORM != 'opal'
7
7
 
8
8
  describe Volt::HttpController do
9
9
  class TestHttpController < Volt::HttpController
10
+ attr_reader :ran_action1
10
11
  attr_reader :action_called
12
+ attr_reader :stoped_action_called
13
+
14
+ before_action :run_action1
15
+ before_action :run_action2, only: [:stoped_action]
11
16
 
12
17
  def just_call_an_action
13
18
  @action_called = true
@@ -45,6 +50,18 @@ if RUBY_PLATFORM != 'opal'
45
50
  def access_body
46
51
  render json: JSON.parse(request.body.read)
47
52
  end
53
+
54
+ def stoped_action
55
+ @stoped_action_called = true
56
+ end
57
+
58
+ def run_action1
59
+ @ran_action1 = true
60
+ end
61
+
62
+ def run_action2
63
+ stop_chain
64
+ end
48
65
  end
49
66
 
50
67
  let(:app) { ->(env) { [404, env, 'app'] } }
@@ -128,5 +145,15 @@ if RUBY_PLATFORM != 'opal'
128
145
  { test: 'params' }.to_json)
129
146
  expect(response.body).to eq({ test: 'params' }.to_json)
130
147
  end
148
+
149
+ it 'should run the before action' do
150
+ controller.perform(:render_plain_text)
151
+ expect(controller.ran_action1).to be(true)
152
+ end
153
+
154
+ it 'should not call the stoped_action' do
155
+ controller.perform(:stoped_action)
156
+ expect(controller.stoped_action_called).to be_nil
157
+ end
131
158
  end
132
159
  end
@@ -186,6 +186,35 @@ describe 'bindings test', type: :feature, sauce: true do
186
186
  end
187
187
  end
188
188
 
189
+ describe 'each binding' do
190
+ it 'should display the last assignment even if the previous assignment resolved afterwards' do
191
+ visit '/'
192
+
193
+ click_link 'Bindings'
194
+
195
+ click_link 'Show with Delay'
196
+
197
+ sleep 0.2
198
+
199
+ expect(find('#eachbinding li:first-child')).to have_content('901')
200
+ expect(page).to have_selector('#eachbinding li', count: 100)
201
+ end
202
+
203
+ it 'should display the last assignment regardless whether the previous promise has already been resolved' do
204
+ visit '/'
205
+
206
+ click_link 'Bindings'
207
+
208
+ click_link 'Show without Delay'
209
+
210
+ sleep 0.2
211
+
212
+ expect(find('#eachbinding li:first-child')).to have_content('1')
213
+ expect(page).to have_selector('#eachbinding li', count: 200)
214
+ end
215
+ end
216
+
217
+
189
218
  describe 'if/unless binding' do
190
219
  it 'should show corret text' do
191
220
  visit '/'
@@ -1,10 +1,10 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe 'store', type: :feature, sauce: true do
4
- it 'should sync between nested root properties on store' do
5
- visit '/store'
3
+ # describe 'store', type: :feature, sauce: true do
4
+ # it 'should sync between nested root properties on store' do
5
+ # visit '/store'
6
6
 
7
- fill_in('field1', with: 'should sync')
8
- expect(find('#field2').value).to eq('should sync')
9
- end
10
- end
7
+ # fill_in('field1', with: 'should sync')
8
+ # expect(find('#field2').value).to eq('should sync')
9
+ # end
10
+ # end
@@ -24,7 +24,7 @@ describe Volt::Associations do
24
24
  end
25
25
 
26
26
  it 'should associate via belongs_to' do
27
- address = store._addresses!.fetch_first.sync
27
+ address = store._addresses!.first.sync
28
28
 
29
29
  expect(address.person.sync.id).to eq(@person.id)
30
30
  end
@@ -591,4 +591,14 @@ describe Volt::Model do
591
591
  'Model does not have a parent and cannot be deleted.')
592
592
  end
593
593
  end
594
+
595
+ describe 'serialization' do
596
+ it 'supports JSON via to_json' do
597
+ model = Volt::Model.new({})
598
+ expect(model.to_json).to eq(model.to_h.to_json)
599
+ expect(model.to_json).to eq(model.to_h.to_json)
600
+ array_model = Volt::ArrayModel.new([model])
601
+ expect(array_model.to_json).to eq(array_model.to_a.to_json)
602
+ end
603
+ end
594
604
  end
@@ -92,7 +92,7 @@ describe 'model permissions' do
92
92
  # Clear the identity map, so we can load up a fresh copy
93
93
  model.save_to.persistor.clear_identity_map
94
94
 
95
- reloaded = store._test_deny_read_names.fetch_first.sync
95
+ reloaded = store._test_deny_read_names.first.sync
96
96
 
97
97
  expect(reloaded._name).to eq(nil)
98
98
  expect(reloaded._other).to eq('should be visible')
@@ -106,13 +106,16 @@ describe 'model permissions' do
106
106
  # Saved
107
107
  count = 0
108
108
 
109
- store._test_deny_deletes.delete(model).then do
109
+ store._test_deny_deletes.delete(model).fail do |err|
110
110
  # deleted
111
111
  count += 1
112
- end
112
+
113
+ match = !!(err =~ /permissions did not allow delete for /)
114
+ expect(match).to eq(true)
115
+ end.sync
113
116
 
114
117
  expect(count).to eq(1)
115
- end
118
+ end.sync
116
119
  end
117
120
 
118
121
  it 'should not check the read permissions when updating (so that all fields are present for the permissions check)' do