jason-rails 0.3.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -1
  3. data/.ruby-version +1 -0
  4. data/Gemfile.lock +184 -0
  5. data/README.md +118 -10
  6. data/app/controllers/jason/api/pusher_controller.rb +15 -0
  7. data/app/controllers/jason/api_controller.rb +78 -0
  8. data/client/babel.config.js +13 -0
  9. data/client/lib/JasonContext.d.ts +6 -1
  10. data/client/lib/JasonProvider.d.ts +6 -5
  11. data/client/lib/JasonProvider.js +5 -97
  12. data/client/lib/actionFactory.js +1 -1
  13. data/client/lib/createActions.d.ts +1 -1
  14. data/client/lib/createActions.js +2 -27
  15. data/client/lib/createJasonReducers.js +49 -3
  16. data/client/lib/createOptDis.d.ts +1 -0
  17. data/client/lib/createOptDis.js +43 -0
  18. data/client/lib/createPayloadHandler.d.ts +9 -1
  19. data/client/lib/createPayloadHandler.js +52 -43
  20. data/client/lib/createServerActionQueue.d.ts +10 -0
  21. data/client/lib/createServerActionQueue.js +48 -0
  22. data/client/lib/createServerActionQueue.test.d.ts +1 -0
  23. data/client/lib/createServerActionQueue.test.js +37 -0
  24. data/client/lib/createTransportAdapter.d.ts +5 -0
  25. data/client/lib/createTransportAdapter.js +20 -0
  26. data/client/lib/deepCamelizeKeys.d.ts +1 -0
  27. data/client/lib/deepCamelizeKeys.js +23 -0
  28. data/client/lib/deepCamelizeKeys.test.d.ts +1 -0
  29. data/client/lib/deepCamelizeKeys.test.js +106 -0
  30. data/client/lib/index.d.ts +6 -5
  31. data/client/lib/pruneIdsMiddleware.d.ts +2 -0
  32. data/client/lib/pruneIdsMiddleware.js +24 -0
  33. data/client/lib/restClient.d.ts +2 -0
  34. data/client/lib/restClient.js +17 -0
  35. data/client/lib/transportAdapters/actionCableAdapter.d.ts +5 -0
  36. data/client/lib/transportAdapters/actionCableAdapter.js +35 -0
  37. data/client/lib/transportAdapters/pusherAdapter.d.ts +5 -0
  38. data/client/lib/transportAdapters/pusherAdapter.js +68 -0
  39. data/client/lib/useJason.d.ts +5 -0
  40. data/client/lib/useJason.js +94 -0
  41. data/client/lib/useJason.test.d.ts +1 -0
  42. data/client/lib/useJason.test.js +85 -0
  43. data/client/lib/useSub.d.ts +1 -1
  44. data/client/lib/useSub.js +6 -3
  45. data/client/package.json +19 -4
  46. data/client/src/JasonProvider.tsx +6 -96
  47. data/client/src/actionFactory.ts +1 -1
  48. data/client/src/createActions.ts +2 -33
  49. data/client/src/createJasonReducers.ts +57 -3
  50. data/client/src/createOptDis.ts +45 -0
  51. data/client/src/createPayloadHandler.ts +58 -47
  52. data/client/src/createServerActionQueue.test.ts +42 -0
  53. data/client/src/createServerActionQueue.ts +47 -0
  54. data/client/src/createTransportAdapter.ts +13 -0
  55. data/client/src/deepCamelizeKeys.test.ts +113 -0
  56. data/client/src/deepCamelizeKeys.ts +17 -0
  57. data/client/src/pruneIdsMiddleware.ts +24 -0
  58. data/client/src/restClient.ts +14 -0
  59. data/client/src/transportAdapters/actionCableAdapter.ts +38 -0
  60. data/client/src/transportAdapters/pusherAdapter.ts +72 -0
  61. data/client/src/useJason.test.ts +87 -0
  62. data/client/src/useJason.ts +110 -0
  63. data/client/src/useSub.ts +6 -3
  64. data/client/yarn.lock +4607 -81
  65. data/config/routes.rb +8 -0
  66. data/jason-rails.gemspec +9 -0
  67. data/lib/jason.rb +40 -1
  68. data/lib/jason/api_model.rb +15 -9
  69. data/lib/jason/broadcaster.rb +19 -0
  70. data/lib/jason/channel.rb +50 -21
  71. data/lib/jason/engine.rb +5 -0
  72. data/lib/jason/graph_helper.rb +165 -0
  73. data/lib/jason/includes_helper.rb +108 -0
  74. data/lib/jason/lua_generator.rb +71 -0
  75. data/lib/jason/publisher.rb +103 -30
  76. data/lib/jason/publisher_old.rb +112 -0
  77. data/lib/jason/subscription.rb +352 -101
  78. data/lib/jason/subscription_old.rb +171 -0
  79. data/lib/jason/version.rb +1 -1
  80. metadata +151 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2883964fb830dbb506a0e845caa3398436d72262f72cc6b295efce14dcc96346
4
- data.tar.gz: 631d00d04195b1e1f48111ca5a6cc02775488e48f94ab5d8d29783a66a6c91a8
3
+ metadata.gz: 453e1a0d649445d973de2e3adf27f88f91c5bf983e65b3c2d993f090816db72b
4
+ data.tar.gz: ec2600988c60bcb6f661b8835cd3bcb7d3a4b740749ab0f21109e9b6a513b4ca
5
5
  SHA512:
6
- metadata.gz: 6de81fcdba79fc067fe7515ad83be895e30d19686e548bfb774026993ab965cf6f0aaa9cebcd4807448e5218c8c9609f414e6ec05d5eb717aef880e3f2e012ea
7
- data.tar.gz: adb6476af69fe904588f55214b930959a0b0d1d2357c3edafca77cc4f8d528a72cb0f72cde6dbec4e097e58d6e5758a2c429419cd9ad60f02d0352dd9abe385b
6
+ metadata.gz: cc2ba8529669a1fe4c48d2ee2ba595a7e3ae907a0d10234f12d68d797de63177143187c5c3767cf32ab35bd053c3c9f3bfa14461f8b6be71d8df437e3e997c7d
7
+ data.tar.gz: c93e0002f3a7e2379ed29483b6a8323b4b492a319ccc4f9948f0638c47e62a9eeb8b416fad2bbe2cc6b5c13f11a103eab462b7a35f5a36145037981e49c99cc6
data/.gitignore CHANGED
@@ -11,4 +11,7 @@
11
11
  .rspec_status
12
12
  client/node_modules/
13
13
 
14
- client/yarn-error.log
14
+ client/yarn-error.log
15
+ *.log
16
+ *.gem
17
+ .DS_Store
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.7.2
data/Gemfile.lock ADDED
@@ -0,0 +1,184 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ jason-rails (0.5.1)
5
+ connection_pool (>= 2.2.3)
6
+ jsondiff
7
+ rails (>= 5)
8
+ redis (>= 4)
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ actioncable (6.1.1)
14
+ actionpack (= 6.1.1)
15
+ activesupport (= 6.1.1)
16
+ nio4r (~> 2.0)
17
+ websocket-driver (>= 0.6.1)
18
+ actionmailbox (6.1.1)
19
+ actionpack (= 6.1.1)
20
+ activejob (= 6.1.1)
21
+ activerecord (= 6.1.1)
22
+ activestorage (= 6.1.1)
23
+ activesupport (= 6.1.1)
24
+ mail (>= 2.7.1)
25
+ actionmailer (6.1.1)
26
+ actionpack (= 6.1.1)
27
+ actionview (= 6.1.1)
28
+ activejob (= 6.1.1)
29
+ activesupport (= 6.1.1)
30
+ mail (~> 2.5, >= 2.5.4)
31
+ rails-dom-testing (~> 2.0)
32
+ actionpack (6.1.1)
33
+ actionview (= 6.1.1)
34
+ activesupport (= 6.1.1)
35
+ rack (~> 2.0, >= 2.0.9)
36
+ rack-test (>= 0.6.3)
37
+ rails-dom-testing (~> 2.0)
38
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
39
+ actiontext (6.1.1)
40
+ actionpack (= 6.1.1)
41
+ activerecord (= 6.1.1)
42
+ activestorage (= 6.1.1)
43
+ activesupport (= 6.1.1)
44
+ nokogiri (>= 1.8.5)
45
+ actionview (6.1.1)
46
+ activesupport (= 6.1.1)
47
+ builder (~> 3.1)
48
+ erubi (~> 1.4)
49
+ rails-dom-testing (~> 2.0)
50
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
51
+ activejob (6.1.1)
52
+ activesupport (= 6.1.1)
53
+ globalid (>= 0.3.6)
54
+ activemodel (6.1.1)
55
+ activesupport (= 6.1.1)
56
+ activerecord (6.1.1)
57
+ activemodel (= 6.1.1)
58
+ activesupport (= 6.1.1)
59
+ activestorage (6.1.1)
60
+ actionpack (= 6.1.1)
61
+ activejob (= 6.1.1)
62
+ activerecord (= 6.1.1)
63
+ activesupport (= 6.1.1)
64
+ marcel (~> 0.3.1)
65
+ mimemagic (~> 0.3.2)
66
+ activesupport (6.1.1)
67
+ concurrent-ruby (~> 1.0, >= 1.0.2)
68
+ i18n (>= 1.6, < 2)
69
+ minitest (>= 5.1)
70
+ tzinfo (~> 2.0)
71
+ zeitwerk (~> 2.3)
72
+ builder (3.2.4)
73
+ coderay (1.1.3)
74
+ concurrent-ruby (1.1.7)
75
+ connection_pool (2.2.3)
76
+ crass (1.0.6)
77
+ diff-lcs (1.4.4)
78
+ erubi (1.10.0)
79
+ globalid (0.4.2)
80
+ activesupport (>= 4.2.0)
81
+ i18n (1.8.7)
82
+ concurrent-ruby (~> 1.0)
83
+ jsondiff (0.0.5)
84
+ loofah (2.8.0)
85
+ crass (~> 1.0.2)
86
+ nokogiri (>= 1.5.9)
87
+ mail (2.7.1)
88
+ mini_mime (>= 0.1.1)
89
+ marcel (0.3.3)
90
+ mimemagic (~> 0.3.2)
91
+ method_source (1.0.0)
92
+ mimemagic (0.3.5)
93
+ mini_mime (1.0.2)
94
+ mini_portile2 (2.5.0)
95
+ minitest (5.14.3)
96
+ nio4r (2.5.4)
97
+ nokogiri (1.11.1)
98
+ mini_portile2 (~> 2.5.0)
99
+ racc (~> 1.4)
100
+ pry (0.13.1)
101
+ coderay (~> 1.1)
102
+ method_source (~> 1.0)
103
+ racc (1.5.2)
104
+ rack (2.2.3)
105
+ rack-test (1.1.0)
106
+ rack (>= 1.0, < 3)
107
+ rails (6.1.1)
108
+ actioncable (= 6.1.1)
109
+ actionmailbox (= 6.1.1)
110
+ actionmailer (= 6.1.1)
111
+ actionpack (= 6.1.1)
112
+ actiontext (= 6.1.1)
113
+ actionview (= 6.1.1)
114
+ activejob (= 6.1.1)
115
+ activemodel (= 6.1.1)
116
+ activerecord (= 6.1.1)
117
+ activestorage (= 6.1.1)
118
+ activesupport (= 6.1.1)
119
+ bundler (>= 1.15.0)
120
+ railties (= 6.1.1)
121
+ sprockets-rails (>= 2.0.0)
122
+ rails-dom-testing (2.0.3)
123
+ activesupport (>= 4.2.0)
124
+ nokogiri (>= 1.6)
125
+ rails-html-sanitizer (1.3.0)
126
+ loofah (~> 2.3)
127
+ railties (6.1.1)
128
+ actionpack (= 6.1.1)
129
+ activesupport (= 6.1.1)
130
+ method_source
131
+ rake (>= 0.8.7)
132
+ thor (~> 1.0)
133
+ rake (12.3.3)
134
+ redis (4.2.5)
135
+ rspec (3.10.0)
136
+ rspec-core (~> 3.10.0)
137
+ rspec-expectations (~> 3.10.0)
138
+ rspec-mocks (~> 3.10.0)
139
+ rspec-core (3.10.0)
140
+ rspec-support (~> 3.10.0)
141
+ rspec-expectations (3.10.0)
142
+ diff-lcs (>= 1.2.0, < 2.0)
143
+ rspec-support (~> 3.10.0)
144
+ rspec-mocks (3.10.0)
145
+ diff-lcs (>= 1.2.0, < 2.0)
146
+ rspec-support (~> 3.10.0)
147
+ rspec-rails (4.0.2)
148
+ actionpack (>= 4.2)
149
+ activesupport (>= 4.2)
150
+ railties (>= 4.2)
151
+ rspec-core (~> 3.10)
152
+ rspec-expectations (~> 3.10)
153
+ rspec-mocks (~> 3.10)
154
+ rspec-support (~> 3.10)
155
+ rspec-support (3.10.0)
156
+ sprockets (4.0.2)
157
+ concurrent-ruby (~> 1.0)
158
+ rack (> 1, < 3)
159
+ sprockets-rails (3.2.2)
160
+ actionpack (>= 4.0)
161
+ activesupport (>= 4.0)
162
+ sprockets (>= 3.0.0)
163
+ sqlite3 (1.4.2)
164
+ thor (1.0.1)
165
+ tzinfo (2.0.4)
166
+ concurrent-ruby (~> 1.0)
167
+ websocket-driver (0.7.3)
168
+ websocket-extensions (>= 0.1.0)
169
+ websocket-extensions (0.1.5)
170
+ zeitwerk (2.4.2)
171
+
172
+ PLATFORMS
173
+ ruby
174
+
175
+ DEPENDENCIES
176
+ jason-rails!
177
+ pry
178
+ rake (~> 12.0)
179
+ rspec (~> 3.0)
180
+ rspec-rails
181
+ sqlite3
182
+
183
+ BUNDLED WITH
184
+ 2.1.4
data/README.md CHANGED
@@ -1,10 +1,12 @@
1
1
  # Jason
2
2
 
3
+ Jason is still in a highly experimental phase with a rapidly changing API. Production use not recommended - but please give it a try!
4
+
3
5
  ## The goal
4
6
 
5
7
  I wanted:
6
8
  - Automatic updates to client state based on database state
7
- - Automatic persistence to database
9
+ - Persistence to the database without many layers of passing parameters
8
10
  - Redux for awesome state management
9
11
  - Optimistic updates
10
12
 
@@ -14,35 +16,141 @@ Jason attempts to minimize this repitition by auto-generating API endpoints, red
14
16
 
15
17
  ## Installation
16
18
 
17
- Add this line to your application's Gemfile:
19
+ Add the gem and the NPM package
18
20
 
19
21
  ```ruby
20
22
  gem 'jason-rails'
21
23
  ```
22
24
 
23
- And then execute:
25
+ ```bash
26
+ yarn add @jamesr2323/jason
27
+ ```
28
+
29
+ You will also need have peer dependencies of `redux`, `react-redux` and `@reduxjs/toolkit`.
30
+
31
+ ### In Rails
32
+
33
+ Include the module `Jason::Publisher` in all models you want to publish via Jason.
34
+
35
+ Create a new initializer e.g. `jason.rb` which defines your schema
36
+
37
+ ```ruby
38
+ Jason.setup do |config|
39
+ config.schema = {
40
+ post: {
41
+ subscribed_fields: [:id, :name]
42
+ },
43
+ comment: {
44
+ subscribed_fields: [:id]
45
+ },
46
+ user: {
47
+ subscribed_fields: [:id]
48
+ }
49
+ }
50
+ end
51
+ ```
24
52
 
25
- $ bundle install
53
+ ### In your frontend code
26
54
 
27
- Or install it yourself as:
55
+ First you need to wrap your root component in a `JasonProvider`.
28
56
 
29
- $ gem install jason
57
+ ```jsx
58
+ import { JasonProvider } from '@jamesr2323/jason'
59
+
60
+ return <JasonProvider>
61
+ <YourApp />
62
+ </JasonProvider>
63
+ ```
64
+
65
+ This is a wrapper around `react-redux` Provider component. This accepts the following props (all optional):
66
+
67
+ - `reducers` - An object of reducers that will be included in `configureStore`. Make sure these do not conflict with the names of any of the models you are configuring for use with Jason
68
+ - `extraActions` - Extra actions you want to be available via the `useAct` hook. (See below)
69
+ This must be a function which returns an object which will be merged with the main Jason actions. The function will be passed a dispatch function, store, axios instance and the Jason actions. For example you can add actions for one of your custom slices:
70
+
71
+ ```js
72
+ function extraActions(dispatch, store, restClient, act) {
73
+ return {
74
+ local: {
75
+ upsert: payload => dis({ type: 'local/upsert', payload })
76
+ }
77
+ }
78
+ }
79
+ ```
80
+
81
+ - `middleware` - Passed directly to `configureStore` with additional Jason middleware
30
82
 
31
83
  ## Usage
84
+ Jason provides two custom hooks to access functionality.
85
+
86
+ ### useAct
87
+ This returns an object which allows you to access actions which both update models on the server, and perform an optimistic update to the Redux store.
32
88
 
33
- TODO: Write usage instructions here
89
+ Example
90
+ ```jsx
91
+ import React, { useState } from 'react'
92
+ import { useAct } from '@jamesr2323/jason'
93
+
94
+ export default function PostCreator() {
95
+ const act = useAct()
96
+ const [name, setName] = useState('')
97
+
98
+ function handleClick() {
99
+ act.posts.add({ name })
100
+ }
101
+
102
+ return <div>
103
+ <input value={name} onChange={e => setName(e.target.value)} />
104
+ <button onClick={handleClick}>Add</button>
105
+ </div>
106
+ }
107
+ ```
108
+
109
+ ### useSub
110
+ This subscribes your Redux store to a model or set of models. It will automatically unsubscribe when the component unmounts.
111
+
112
+ Example
113
+ ```jsx
114
+ import React from 'react'
115
+ import { useSelector } from 'react-redux'
116
+ import { useSub } from '@jamesr2323/jason'
117
+ import _ from 'lodash'
118
+
119
+ export default function PostsList() {
120
+ useSub({ model: 'post', includes: ['comments'] })
121
+ const posts = useSelector(s => _.values(s.posts.entities))
122
+
123
+ return <div>
124
+ { posts.map(({ id, name }) => <div key={id}>{ name }</div>) }
125
+ </div>
126
+ }
127
+ ```
128
+
129
+ ## Authorization
130
+
131
+ By default all models can be subscribed to and updated without authentication or authorization. Probably you want to lock down access.
132
+
133
+ ### Authorizing subscriptions
134
+ You can do this by providing an class to Jason in the initializer under the `subscription_authorization_service` key. This must be a class receiving a message `call` with the parameters `user`, `model`, `conditions`, `sub_models` and return true or false for whether the user is allowed to access a subscription with those parameters. You can decide the implementation details of this to be as simple or complex as your app requires.
135
+
136
+ ### Authorizing updates
137
+ Similarly to authorizing subscriptions, you can do this by providing an class to Jason in the initializer under the `update_authorization_service` key. This must be a class receiving a message `call` with the parameters `user`, `model`, `instance`, `update`, `remove` and return true or false for whether the user is allowed to access a subscription with those parameters.
138
+
139
+ ## Roadmap
140
+
141
+ Development is primarily driven by the needs of projects we're using Jason in. In no particular order, being considered is:
142
+ - Failure handling - rolling back local state in case of an error on the server
143
+ - Authorization - more thorough authorization integration, with utility functions for common authorizations. Allowing authorization of access to particular fields such as restricting the fields of a user that are publicly broadcast.
144
+ - Utilities for "Draft editing" - both storing client-side copies of model trees which can be committed or discarded, as well as persisting a shadow copy to the database (to allow resumable editing, or possibly collaborative editing features)
34
145
 
35
146
  ## Development
36
147
 
37
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
38
148
 
39
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
40
149
 
41
150
  ## Contributing
42
151
 
43
152
  Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/jason. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/jason/blob/master/CODE_OF_CONDUCT.md).
44
153
 
45
-
46
154
  ## License
47
155
 
48
156
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,15 @@
1
+ class Jason::Api::PusherController < ApplicationController
2
+ skip_before_action :verify_authenticity_token
3
+
4
+ def auth
5
+ channel_main_name = params[:channel_name].remove("private-#{Jason.pusher_channel_prefix}-")
6
+ subscription_id = channel_main_name.remove('jason-')
7
+
8
+ if Jason::Subscription.find_by_id(subscription_id).user_can_access?(current_user)
9
+ response = Pusher.authenticate(params[:channel_name], params[:socket_id])
10
+ return render json: response
11
+ else
12
+ return head :forbidden
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,78 @@
1
+ class Jason::ApiController < ::ApplicationController
2
+ before_action :load_and_authorize_subscription, only: [:create_subscription, :remove_subscription, :get_payload]
3
+ # config seems to be a reserved name, resulting in infinite loop
4
+ def configuration
5
+ payload = {
6
+ schema: Jason.schema,
7
+ transportService: Jason.transport_service,
8
+ }
9
+
10
+ if Jason.transport_service == :pusher
11
+ payload.merge!({
12
+ pusherKey: Jason.pusher_key,
13
+ pusherRegion: Jason.pusher_region,
14
+ pusherChannelPrefix: Jason.pusher_channel_prefix
15
+ })
16
+ end
17
+
18
+ render json: payload
19
+ end
20
+
21
+ def action
22
+ type = params[:type]
23
+ entity = type.split('/')[0].underscore
24
+ api_model = Jason::ApiModel.new(entity.singularize)
25
+ model = entity.singularize.camelize.constantize
26
+ action = type.split('/')[1].underscore
27
+
28
+ if action == 'move_priority'
29
+ id, priority = params[:payload].values_at(:id, :priority)
30
+
31
+ instance = model.find(id)
32
+ priority_filter = instance.as_json.with_indifferent_access.slice(*api_model.priority_scope)
33
+
34
+ all_instance_ids = model.send(api_model.scope || :all).where(priority_filter).where.not(id: instance.id).order(:priority).pluck(:id)
35
+ all_instance_ids.insert(priority.to_i, instance.id)
36
+
37
+ all_instance_ids.each_with_index do |id, i|
38
+ model.find(id).update!(priority: i, skip_publish_json: true)
39
+ end
40
+
41
+ model.publish_all(model.find(all_instance_ids))
42
+ elsif action == 'upsert' || action == 'add'
43
+ payload = api_model.permit(params)
44
+ return render json: model.find_or_create_by_id!(payload).as_json(api_model.as_json_config)
45
+ elsif action == 'remove'
46
+ model.find(params[:payload]).destroy!
47
+ end
48
+
49
+ return head :ok
50
+ end
51
+
52
+ def create_subscription
53
+ @subscription.add_consumer(params[:consumer_id])
54
+ render json: { channelName: @subscription.channel }
55
+ end
56
+
57
+ def remove_subscription
58
+ @subscription.remove_consumer(params[:consumer_id])
59
+ end
60
+
61
+ def get_payload
62
+ if params[:options].try(:[], :force_refresh)
63
+ @subscription.set_ids_for_sub_models
64
+ end
65
+
66
+ render json: @subscription.get
67
+ end
68
+
69
+ private
70
+
71
+ def load_and_authorize_subscription
72
+ config = params[:config].to_unsafe_h
73
+ @subscription = Jason::Subscription.upsert_by_config(config['model'], conditions: config['conditions'], includes: config['includes'])
74
+ if !@subscription.user_can_access?(current_user)
75
+ return head :forbidden
76
+ end
77
+ end
78
+ end