volt 0.9.3.pre3 → 0.9.3.pre4

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 (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