apicasso 0.4.5 → 0.4.6

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 (79) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +226 -223
  3. data/Rakefile +23 -23
  4. data/app/controllers/apicasso/apidocs_controller.rb +309 -299
  5. data/app/controllers/apicasso/application_controller.rb +170 -147
  6. data/app/controllers/apicasso/crud_controller.rb +246 -245
  7. data/app/controllers/concerns/orderable.rb +45 -45
  8. data/app/models/apicasso/ability.rb +38 -38
  9. data/app/models/apicasso/application_record.rb +6 -6
  10. data/app/models/apicasso/key.rb +25 -25
  11. data/app/models/apicasso/request.rb +8 -8
  12. data/config/routes.rb +14 -14
  13. data/lib/apicasso/active_record_extension.rb +0 -44
  14. data/lib/apicasso/engine.rb +13 -13
  15. data/lib/apicasso/version.rb +3 -3
  16. data/lib/apicasso.rb +15 -13
  17. data/lib/generators/apicasso/install/install_generator.rb +25 -25
  18. data/lib/generators/apicasso/install/templates/create_apicasso_tables.rb +20 -20
  19. data/spec/dummy/Gemfile +56 -0
  20. data/spec/dummy/Gemfile.lock +237 -0
  21. data/spec/dummy/app/controllers/application_controller.rb +1 -1
  22. data/spec/dummy/app/models/used_model.rb +42 -0
  23. data/spec/dummy/app/serializers/used_model_serializer.rb +3 -0
  24. data/spec/dummy/bin/rails +5 -0
  25. data/spec/dummy/bin/rake +5 -0
  26. data/spec/dummy/bin/setup +0 -3
  27. data/spec/dummy/bin/spring +17 -0
  28. data/spec/dummy/bin/update +0 -3
  29. data/spec/dummy/config/application.rb +14 -10
  30. data/spec/dummy/config/cable.yml +1 -1
  31. data/spec/dummy/config/credentials.yml.enc +1 -0
  32. data/spec/dummy/config/database.yml +5 -14
  33. data/spec/dummy/config/environments/development.rb +6 -10
  34. data/spec/dummy/config/environments/production.rb +1 -10
  35. data/spec/dummy/config/initializers/cors.rb +16 -0
  36. data/spec/dummy/config/locales/en.yml +7 -32
  37. data/spec/dummy/config/routes.rb +1 -1
  38. data/{db/migrate/20180826141433_create_apicasso_tables.rb → spec/dummy/db/migrate/20180918134607_create_apicasso_tables.rb} +1 -0
  39. data/spec/dummy/db/migrate/20180918141254_create_used_models.rb +44 -0
  40. data/spec/dummy/db/migrate/20180919130152_create_active_storage_tables.active_storage.rb +26 -0
  41. data/spec/dummy/db/migrate/20180920133933_change_used_model_to_validates.rb +7 -0
  42. data/spec/dummy/db/schema.rb +98 -0
  43. data/spec/dummy/db/seeds.rb +56 -0
  44. data/spec/factories/used_model.rb +28 -0
  45. data/spec/models/used_model_spec.rb +35 -0
  46. data/spec/rails_helper.rb +66 -0
  47. data/spec/requests/requests_spec.rb +227 -0
  48. data/spec/spec_helper.rb +7 -9
  49. data/spec/support/factory_bot.rb +3 -0
  50. metadata +83 -64
  51. data/spec/controllers/apicasso/aplication_controller_spec.rb +0 -18
  52. data/spec/controllers/apicasso/crud_controller_spec.rb +0 -107
  53. data/spec/dummy/app/assets/config/manifest.js +0 -3
  54. data/spec/dummy/app/assets/javascripts/application.js +0 -15
  55. data/spec/dummy/app/assets/javascripts/cable.js +0 -13
  56. data/spec/dummy/app/assets/stylesheets/application.css +0 -15
  57. data/spec/dummy/app/channels/application_cable/channel.rb +0 -4
  58. data/spec/dummy/app/channels/application_cable/connection.rb +0 -4
  59. data/spec/dummy/app/helpers/application_helper.rb +0 -2
  60. data/spec/dummy/app/jobs/application_job.rb +0 -2
  61. data/spec/dummy/app/mailers/application_mailer.rb +0 -4
  62. data/spec/dummy/app/views/layouts/application.html.erb +0 -15
  63. data/spec/dummy/app/views/layouts/mailer.html.erb +0 -13
  64. data/spec/dummy/app/views/layouts/mailer.text.erb +0 -1
  65. data/spec/dummy/bin/yarn +0 -11
  66. data/spec/dummy/config/initializers/assets.rb +0 -14
  67. data/spec/dummy/config/initializers/content_security_policy.rb +0 -25
  68. data/spec/dummy/config/initializers/cookies_serializer.rb +0 -5
  69. data/spec/dummy/log/development.log +0 -0
  70. data/spec/dummy/public/404.html +0 -67
  71. data/spec/dummy/public/422.html +0 -67
  72. data/spec/dummy/public/500.html +0 -66
  73. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  74. data/spec/dummy/public/apple-touch-icon.png +0 -0
  75. data/spec/dummy/public/favicon.ico +0 -0
  76. data/spec/factories/apicasso_key.rb +0 -9
  77. data/spec/factories/object.rb +0 -5
  78. data/spec/models/apicasso/key.rb +0 -5
  79. data/spec/routing/appointments_routing_spec.rb +0 -38
@@ -1,245 +1,246 @@
1
- # frozen_string_literal: true
2
-
3
- module Apicasso
4
- # Controller to consume read-only data to be used on client's frontend
5
- class CrudController < Apicasso::ApplicationController
6
- before_action :set_root_resource
7
- before_action :set_object, except: %i[index create schema]
8
- before_action :set_nested_resource, only: %i[nested_index]
9
- before_action :set_records, only: %i[index nested_index]
10
-
11
- include Orderable
12
-
13
- # GET /:resource
14
- # Returns a paginated, ordered and filtered query based response.
15
- # Consider this
16
- # To get all `Channel` sorted by ascending `name` , filtered by
17
- # the ones that have a `domain` that matches exactly `"domain.com"`,
18
- # paginating records 42 per page and retrieving the page 42.
19
- # Example:
20
- # GET /sites?sort=+name,-updated_at&q[domain_eq]=domain.com&page=42&per_page=42
21
- def index
22
- set_access_control_headers
23
- render json: index_json
24
- end
25
-
26
- # GET /:resource/1
27
- # Common behavior for showing a record, with an addition of
28
- # relation/methods including on response
29
- def show
30
- set_access_control_headers
31
- render json: show_json
32
- end
33
-
34
- # PATCH/PUT /:resource/1
35
- # Common behavior for an update API endpoint
36
- def update
37
- authorize_for(action: :update,
38
- resource: resource.name.underscore.to_sym,
39
- object: @object)
40
- if @object.update(object_params)
41
- render json: @object
42
- else
43
- render json: @object.errors, status: :unprocessable_entity
44
- end
45
- end
46
-
47
- # DELETE /:resource/1
48
- # Common behavior for an destroy API endpoint
49
- def destroy
50
- authorize_for(action: :destroy,
51
- resource: resource.name.underscore.to_sym,
52
- object: @object)
53
- if @object.destroy
54
- head :no_content, status: :ok
55
- else
56
- render json: @object.errors, status: :unprocessable_entity
57
- end
58
- end
59
-
60
- # GET /:resource/1/:nested_resource
61
- alias nested_index index
62
-
63
- # POST /:resource
64
- def create
65
- @object = resource.new(object_params)
66
- authorize_for(action: :create,
67
- resource: resource.name.underscore.to_sym,
68
- object: @object)
69
- if @object.save
70
- render json: @object, status: :created
71
- else
72
- render json: @object.errors, status: :unprocessable_entity
73
- end
74
- end
75
-
76
- # OPTIONS /:resource
77
- # OPTIONS /:resource/1/:nested_resource
78
- # Will return a JSON with the schema of the current resource, using
79
- # attribute names as keys and attirbute types as values.
80
- def schema
81
- render json: resource_schema.to_json unless preflight?
82
- end
83
-
84
- private
85
-
86
- # Common setup to stablish which model is the resource of this request
87
- def set_root_resource
88
- @root_resource = params[:resource].classify.constantize
89
- end
90
-
91
- # Common setup to stablish which object this request is querying
92
- def set_object
93
- id = params[:id]
94
- @object = resource.friendly.find(id)
95
- rescue NoMethodError
96
- @object = resource.find(id)
97
- ensure
98
- authorize! :read, @object
99
- end
100
-
101
- # Setup to stablish the nested model to be queried
102
- def set_nested_resource
103
- @nested_resource = @object.send(params[:nested].underscore.pluralize)
104
- end
105
-
106
- # Reutrns root_resource if nested_resource is not set scoped by permissions
107
- def resource
108
- (@nested_resource || @root_resource)
109
- end
110
-
111
- # Used to setup the resource's schema, mapping attributes and it's types
112
- def resource_schema
113
- schemated = {}
114
- resource.columns_hash.each { |key, value| schemated[key] = value.type }
115
- schemated
116
- end
117
-
118
- # Used to setup the records from the selected resource that are
119
- # going to be rendered, if authorized
120
- def set_records
121
- authorize! :read, resource.name.underscore.to_sym
122
- @records = resource.ransack(parsed_query).result
123
- key_scope_records
124
- reorder_records if params[:sort].present?
125
- select_fields if params[:select].present?
126
- include_relations if params[:include].present?
127
- end
128
-
129
- # Selects a fieldset that should be returned, instead of all fields
130
- # from records.
131
- def select_fields
132
- @records = @records.select(*params[:select].split(','))
133
- end
134
-
135
- # Reordering of records which happens when receiving `params[:sort]`
136
- def reorder_records
137
- @records = @records.unscope(:order).order(ordering_params(params))
138
- end
139
-
140
- # Raw paginated records object
141
- def paginated_records
142
- @records
143
- .paginate(page: params[:page], per_page: params[:per_page])
144
- end
145
-
146
- # Records that can be accessed from current Apicasso::Key scope
147
- # permissions
148
- def key_scope_records
149
- @records = @records.accessible_by(current_ability).unscope(:order)
150
- end
151
-
152
- # The response for index action, which can be a pagination of a record collection
153
- # or a grouped count of attributes
154
- def index_json
155
- if params[:group].present?
156
- @records.group(params[:group][:by].split(',')).send(:calculate, params[:group][:calculate], params[:group][:field])
157
- else
158
- collection_response
159
- end
160
- end
161
-
162
- # The response for show action, which can be a fieldset
163
- # or a full response of attributes
164
- def show_json
165
- if params[:select].present?
166
- @object.to_json(include: parsed_include, only: parsed_select)
167
- else
168
- @object.to_json(include: parsed_include)
169
- end
170
- end
171
-
172
- # Parsing of `paginated_records` with pagination variables metadata
173
- def built_paginated
174
- { entries: paginated_records }.merge(pagination_metadata_for(paginated_records))
175
- end
176
-
177
- # All records matching current query and it's total
178
- def built_unpaginated
179
- { entries: @records, total: @records.size }
180
- end
181
-
182
- # Parsed JSON to be used as response payload, with included relations
183
- def include_relations
184
- @records = JSON.parse(included_collection.to_json(include: parsed_include))
185
- rescue ActiveRecord::AssociationNotFoundError, ActiveRecord::ConfigurationError
186
- @records = JSON.parse(@records.to_json(include: parsed_include))
187
- end
188
-
189
- # A way to SQL-include for current param[:include], only if available
190
- def included_collection
191
- @records.includes(parsed_include)
192
- rescue ActiveRecord::AssociationNotFoundError
193
- @records
194
- end
195
-
196
- # Returns the collection checking if it needs pagination
197
- def collection_response
198
- if params[:per_page].to_i < 0
199
- built_unpaginated
200
- else
201
- built_paginated
202
- end
203
- end
204
-
205
- # Only allow a trusted parameter "white list" through,
206
- # based on resource's schema.
207
- def object_params
208
- params.require(resource.name.underscore.to_sym)
209
- .permit(resource_params)
210
- end
211
-
212
- # Resource params mapping, with a twist:
213
- # Including relations as they are needed
214
- def resource_params
215
- built = resource_schema.keys
216
- built += has_one_params if has_one_params.present?
217
- built += has_many_params if has_many_params.present?
218
- built
219
- end
220
-
221
- # A wrapper to has_one relations parameter building
222
- def has_one_params
223
- resource.reflect_on_all_associations(:has_one).map do |one|
224
- if one.class_name.starts_with?('ActiveStorage')
225
- next if one.class_name.ends_with?('Blob')
226
- one.name.to_s.gsub(/(_attachment)$/, '').to_sym
227
- else
228
- one.name
229
- end
230
- end.compact
231
- end
232
-
233
- # A wrapper to has_many parameter building
234
- def has_many_params
235
- resource.reflect_on_all_associations(:has_many).map do |many|
236
- if many.class_name.starts_with?('ActiveStorage')
237
- next if many.class_name.ends_with?('Blob')
238
- { many.name.to_s.gsub(/(_attachments)$/, '').to_sym => [] }
239
- else
240
- { many.name.to_sym => [] }
241
- end
242
- end.compact
243
- end
244
- end
245
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Apicasso
4
+ # Controller to consume read-only data to be used on client's frontend
5
+ class CrudController < Apicasso::ApplicationController
6
+ before_action :set_root_resource
7
+ before_action :set_object, except: %i[index create schema]
8
+ before_action :set_nested_resource, only: %i[nested_index]
9
+ before_action :set_records, only: %i[index nested_index]
10
+ include Orderable
11
+ # GET /:resource
12
+ # Returns a paginated, ordered and filtered query based response.
13
+ # Consider this
14
+ # To get all `Channel` sorted by ascending `name` , filtered by
15
+ # the ones that have a `domain` that matches exactly `"domain.com"`,
16
+ # paginating records 42 per page and retrieving the page 42.
17
+ # Example:
18
+ # GET /sites?sort=+name,-updated_at&q[domain_eq]=domain.com&page=42&per_page=42
19
+ def index
20
+ set_access_control_headers
21
+ render json: index_json
22
+ end
23
+
24
+ # GET /:resource/1
25
+ # Common behavior for showing a record, with an addition of
26
+ # relation/methods including on response
27
+ def show
28
+ set_access_control_headers
29
+ render json: show_json
30
+ end
31
+
32
+ # PATCH/PUT /:resource/1
33
+ # Common behavior for an update API endpoint
34
+ def update
35
+ authorize_for(action: :update,
36
+ resource: resource.name.underscore.to_sym,
37
+ object: @object)
38
+ if @object.update(object_params)
39
+ render json: @object.to_json
40
+ else
41
+ render json: @object.errors, status: :unprocessable_entity
42
+ end
43
+ end
44
+
45
+ # DELETE /:resource/1
46
+ # Common behavior for an destroy API endpoint
47
+ def destroy
48
+ authorize_for(action: :destroy,
49
+ resource: resource.name.underscore.to_sym,
50
+ object: @object)
51
+ if @object.destroy
52
+ head :no_content, status: :ok
53
+ else
54
+ render json: @object.errors, status: :unprocessable_entity
55
+ end
56
+ end
57
+
58
+ # GET /:resource/1/:nested_resource
59
+ alias nested_index index
60
+
61
+ # POST /:resource
62
+ def create
63
+ @object = resource.new(object_params)
64
+ authorize_for(action: :create,
65
+ resource: resource.name.underscore.to_sym,
66
+ object: @object)
67
+ if @object.save
68
+ render json: @object.to_json, status: :created
69
+ else
70
+ render json: @object.errors, status: :unprocessable_entity
71
+ end
72
+ end
73
+
74
+ # OPTIONS /:resource
75
+ # OPTIONS /:resource/1/:nested_resource
76
+ # Will return a JSON with the schema of the current resource, using
77
+ # attribute names as keys and attirbute types as values.
78
+ def schema
79
+ render json: resource_schema.to_json unless preflight?
80
+ end
81
+
82
+ private
83
+
84
+ # Common setup to stablish which model is the resource of this request
85
+ def set_root_resource
86
+ @root_resource = params[:resource].classify.constantize
87
+ end
88
+
89
+ # Common setup to stablish which object this request is querying
90
+ def set_object
91
+ id = params[:id]
92
+ @object = resource.friendly.find(id)
93
+ rescue NoMethodError
94
+ @object = resource.find(id)
95
+ ensure
96
+ authorize! :read, @object
97
+ end
98
+
99
+ # Setup to stablish the nested model to be queried
100
+ def set_nested_resource
101
+ @nested_resource = @object.send(params[:nested].underscore.pluralize)
102
+ end
103
+
104
+ # Reutrns root_resource if nested_resource is not set scoped by permissions
105
+ def resource
106
+ (@nested_resource || @root_resource)
107
+ end
108
+
109
+ # Used to setup the resource's schema, mapping attributes and it's types
110
+ def resource_schema
111
+ schemated = {}
112
+ resource.columns_hash.each { |key, value| schemated[key] = value.type }
113
+ schemated
114
+ end
115
+
116
+ # Used to setup the records from the selected resource that are
117
+ # going to be rendered, if authorized
118
+ def set_records
119
+ authorize! :read, resource.name.underscore.to_sym
120
+ @records = resource.ransack(parsed_query).result
121
+ @object = resource.new
122
+ key_scope_records
123
+ reorder_records if params[:sort].present?
124
+ select_fields if params[:select].present?
125
+ include_relations if params[:include].present?
126
+ end
127
+
128
+ # Selects a fieldset that should be returned, instead of all fields
129
+ # from records.
130
+ def select_fields
131
+ @records = @records.select(*parsed_select)
132
+ end
133
+
134
+ # Reordering of records which happens when receiving `params[:sort]`
135
+ def reorder_records
136
+ @records = @records.unscope(:order).order(ordering_params(params))
137
+ end
138
+
139
+ # Raw paginated records object
140
+ def paginated_records
141
+ @records
142
+ .paginate(page: params[:page], per_page: params[:per_page])
143
+ end
144
+
145
+ # Records that can be accessed from current Apicasso::Key scope
146
+ # permissions
147
+ def key_scope_records
148
+ @records = @records.accessible_by(current_ability).unscope(:order)
149
+ end
150
+
151
+ # The response for index action, which can be a pagination of a record collection
152
+ # or a grouped count of attributes
153
+ def index_json
154
+ if params[:group].present?
155
+ @records.group(params[:group][:by].split(','))
156
+ .send(:calculate,
157
+ params[:group][:calculate],
158
+ params[:group][:field])
159
+ else
160
+ collection_response
161
+ end
162
+ end
163
+
164
+ # The response for show action, which can be a fieldset
165
+ # or a full response of attributes
166
+ def show_json
167
+ json_hash = include_options
168
+ json_hash[:only] = parsed_select if params[:select].present?
169
+ @object.as_json(json_hash)
170
+ end
171
+
172
+ # Parsing of `paginated_records` with pagination variables metadata
173
+ def built_paginated
174
+ { entries: paginated_records.as_json(include_options) }
175
+ .merge(pagination_metadata_for(paginated_records))
176
+ end
177
+
178
+ # All records matching current query and it's total
179
+ def built_unpaginated
180
+ { entries: @records.as_json(include_options),
181
+ total: @records.size }
182
+ end
183
+
184
+ # Parse to include options
185
+ def include_options
186
+ { include: parsed_associations || [],
187
+ methods: parsed_methods || [] }
188
+ end
189
+
190
+ # Parsed JSON to be used as response payload, with included relations
191
+ def include_relations
192
+ @records = @records.includes(parsed_associations)
193
+ end
194
+
195
+ # Returns the collection checking if it needs pagination
196
+ def collection_response
197
+ if params[:per_page].to_i < 0
198
+ built_unpaginated
199
+ else
200
+ built_paginated
201
+ end
202
+ end
203
+
204
+ # Only allow a trusted parameter "white list" through,
205
+ # based on resource's schema.
206
+ def object_params
207
+ params.require(resource.name.underscore.to_sym)
208
+ .permit(resource_params)
209
+ end
210
+
211
+ # Resource params mapping, with a twist:
212
+ # Including relations as they are needed
213
+ def resource_params
214
+ built = resource_schema.keys
215
+ built += has_one_params if has_one_params.present?
216
+ built += has_many_params if has_many_params.present?
217
+ built
218
+ end
219
+
220
+ # A wrapper to has_one relations parameter building
221
+ def has_one_params
222
+ resource.reflect_on_all_associations(:has_one).map do |one|
223
+ if one.class_name.starts_with?('ActiveStorage')
224
+ next if one.class_name.ends_with?('Blob')
225
+
226
+ one.name.to_s.gsub(/(_attachment)$/, '').to_sym
227
+ else
228
+ one.name
229
+ end
230
+ end.compact
231
+ end
232
+
233
+ # A wrapper to has_many parameter building
234
+ def has_many_params
235
+ resource.reflect_on_all_associations(:has_many).map do |many|
236
+ if many.class_name.starts_with?('ActiveStorage')
237
+ next if many.class_name.ends_with?('Blob')
238
+
239
+ { many.name.to_s.gsub(/(_attachments)$/, '').to_sym => [] }
240
+ else
241
+ { many.name.to_sym => [] }
242
+ end
243
+ end.compact
244
+ end
245
+ end
246
+ end
@@ -1,45 +1,45 @@
1
- # frozen_string_literal: true
2
-
3
- # This concern is used to provide abstract ordering based on `params[:sort]`
4
- module Orderable
5
- extend ActiveSupport::Concern
6
- SORT_ORDER = { '+' => :asc, '-' => :desc }.freeze
7
-
8
- # A list of the param names that can be used for ordering the model list
9
- def ordering_params(params)
10
- # For example it retrieves a list of orders in descending order of total_value.
11
- # Within a specific total_value, older orders are ordered first
12
- #
13
- # GET /orders?sort=-total_value,created_at
14
- # ordering_params(params) # => { total_value: :desc, created_at: :asc }
15
- #
16
- # Usage:
17
- # Order.order(ordering_params(params))
18
- ordering = {}
19
- params[:sort].try(:split, ',').try(:each) do |attr|
20
- parsed_attr = parse_attr attr
21
- if model.attribute_names.include?(parsed_attr)
22
- ordering[parsed_attr] = SORT_ORDER[parse_sign attr]
23
- end
24
- end
25
- ordering
26
- end
27
-
28
- private
29
-
30
- # Parsing of attributes to avoid empty starts in case browser passes "+" as " "
31
- def parse_attr(attr)
32
- return attr.gsub(/^\ (.*)/, '\1') if attr.starts_with?(' ')
33
- return attr[1..-1] if attr.match?(/\A[+-]/)
34
- attr
35
- end
36
-
37
- # Ordering sign parse, which separates
38
- def parse_sign(attr)
39
- attr.match?(/\A[+-]/) ? attr.slice!(0) : '+'
40
- end
41
-
42
- def model
43
- (params[:nested] || params[:resource] || controller_name).classify.constantize
44
- end
45
- end
1
+ # frozen_string_literal: true
2
+
3
+ # This concern is used to provide abstract ordering based on `params[:sort]`
4
+ module Orderable
5
+ extend ActiveSupport::Concern
6
+ SORT_ORDER = { '+' => :asc, '-' => :desc }.freeze
7
+
8
+ # A list of the param names that can be used for ordering the model list
9
+ def ordering_params(params)
10
+ # For example it retrieves a list of orders in descending order of total_value.
11
+ # Within a specific total_value, older orders are ordered first
12
+ #
13
+ # GET /orders?sort=-total_value,created_at
14
+ # ordering_params(params) # => { total_value: :desc, created_at: :asc }
15
+ #
16
+ # Usage:
17
+ # Order.order(ordering_params(params))
18
+ ordering = {}
19
+ params[:sort]&.delete(' ').try(:split, ',').try(:each) do |attr|
20
+ parsed_attr = parse_attr attr
21
+ if model.attribute_names.include?(parsed_attr)
22
+ ordering[parsed_attr] = SORT_ORDER[parse_sign attr]
23
+ end
24
+ end
25
+ ordering
26
+ end
27
+
28
+ private
29
+
30
+ # Parsing of attributes to avoid empty starts in case browser passes "+" as " "
31
+ def parse_attr(attr)
32
+ return attr.gsub(/^\ (.*)/, '\1') if attr.starts_with?(' ')
33
+ return attr[1..-1] unless attr.match(/\A[+-]/).nil?
34
+ attr
35
+ end
36
+
37
+ # Ordering sign parse, which separates
38
+ def parse_sign(attr)
39
+ attr.match(/\A[+-]/).nil? ? '+': attr.slice!(0)
40
+ end
41
+
42
+ def model
43
+ (params[:nested] || params[:resource] || controller_name).classify.constantize
44
+ end
45
+ end
@@ -1,38 +1,38 @@
1
- # frozen_string_literal: true
2
-
3
- module Apicasso
4
- # Ability to parse a scope object from Apicasso::Key
5
- class Ability
6
- include CanCan::Ability
7
-
8
- def initialize(key)
9
- key ||= Apicasso::Key.new
10
- cannot :manage, :all
11
- cannot :read, :all
12
- key.scope.each do |permission, klasses_clearances|
13
- klasses_clearances.each do |klass, clearance|
14
- if clearance == true
15
- # Usage:
16
- # To have a key reading all channels and all accouts
17
- # you would have a scope:
18
- # => `{read: {channel: true, accout: true}}`
19
- can permission.to_sym, klass.underscore.singularize.to_sym
20
- can permission.to_sym, klass.classify.constantize
21
- elsif clearance.class == Hash
22
- # Usage:
23
- # To have a key reading all banners from a channel with id 999
24
- # you would have a scope:
25
- # => `{read: {banner: {owner_id: [999]}}}`
26
- can permission.to_sym,
27
- klass.underscore.singularize.to_sym
28
- clearance.each do |by_field, values|
29
- can permission.to_sym,
30
- klass.classify.constantize,
31
- by_field.to_sym => values
32
- end
33
- end
34
- end
35
- end
36
- end
37
- end
38
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Apicasso
4
+ # Ability to parse a scope object from Apicasso::Key
5
+ class Ability
6
+ include CanCan::Ability
7
+
8
+ def initialize(key)
9
+ key ||= Apicasso::Key.new
10
+ cannot :manage, :all
11
+ cannot :read, :all
12
+ key.scope.each do |permission, klasses_clearances|
13
+ klasses_clearances.each do |klass, clearance|
14
+ if clearance == true
15
+ # Usage:
16
+ # To have a key reading all channels and all accouts
17
+ # you would have a scope:
18
+ # => `{read: {channel: true, accout: true}}`
19
+ can permission.to_sym, klass.underscore.singularize.to_sym
20
+ can permission.to_sym, klass.classify.constantize
21
+ elsif clearance.class == Hash
22
+ # Usage:
23
+ # To have a key reading all banners from a channel with id 999
24
+ # you would have a scope:
25
+ # => `{read: {banner: {owner_id: [999]}}}`
26
+ can permission.to_sym,
27
+ klass.underscore.singularize.to_sym
28
+ clearance.each do |by_field, values|
29
+ can permission.to_sym,
30
+ klass.classify.constantize,
31
+ by_field.to_sym => values
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end