action_policy 0.3.1 → 0.3.2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +3 -1
- data/docs/README.md +8 -1
- data/docs/assets/docsify-search.js +364 -0
- data/docs/assets/styles.css +32 -2
- data/docs/custom_policy.md +1 -0
- data/docs/decorators.md +1 -1
- data/docs/index.html +5 -1
- data/lib/action_policy.rb +4 -1
- data/lib/action_policy/policy/scoping.rb +13 -13
- data/lib/action_policy/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 998666ad64277112c6ddc44e6db9e0aa81b6bbf32994fb675797bcd8e1f9ca70
|
4
|
+
data.tar.gz: 5a35c722589841c6b41d6efd1735abe63833db03b9a7aec23e798648a97c5470
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 03a5594069a5947566708f3b3ad419d79be8931d38c25195f754d1474fa9b2f3bcf5e0f9e520a828d56c1fcfac762929ae4fdcf852c657f4b6bec973c0559e0b
|
7
|
+
data.tar.gz: e41e88d916fdf0832e7b03cdadcf7f6c1a4814c45e6ca76e12094bbcb03a6ea54146a0e676d079a0813caff31f54c3dfe90d0e917ade8c4261e17ec4d55a4221
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
## master
|
2
2
|
|
3
|
+
## 0.3.2 (2019-05-26) 👶
|
4
|
+
|
5
|
+
- Fixed thread-safety issues with scoping configs. ([@palkan][])
|
6
|
+
|
7
|
+
Fixes [#75](https://github.com/palkan/action_policy/issues/75).
|
8
|
+
|
3
9
|
## 0.3.1 (2019-05-30)
|
4
10
|
|
5
11
|
- Fixed bug with missing implicit target and hash like scoping data. ([@palkan][])
|
data/README.md
CHANGED
@@ -4,7 +4,9 @@
|
|
4
4
|
|
5
5
|
# ActionPolicy
|
6
6
|
|
7
|
-
|
7
|
+
Authorization framework for Ruby and Rails applications.
|
8
|
+
|
9
|
+
Composable. Extensible. Performant.
|
8
10
|
|
9
11
|
📑 [Documentation](https://actionpolicy.evilmartians.io)
|
10
12
|
|
data/docs/README.md
CHANGED
@@ -3,7 +3,8 @@
|
|
3
3
|
|
4
4
|
## Action Policy
|
5
5
|
|
6
|
-
>
|
6
|
+
> Authorization framework for Ruby and Rails.
|
7
|
+
<br>Composable. Extensible. Performant.
|
7
8
|
|
8
9
|
**NOTE:** this documentation is for the version "0.3.0+".
|
9
10
|
|
@@ -20,6 +21,12 @@ Action Policy provides flexible tools to build an _authorization layer_ for your
|
|
20
21
|
|
21
22
|
**NOTE:** Action Policy does not force you to use a specific authorization model (i.e., roles, permissions, etc.) and does not provide one. It only answers a single question: **How to verify access?**
|
22
23
|
|
24
|
+
## Where to go from here?
|
25
|
+
- [Quick start](./quick_start.md)
|
26
|
+
- [Using with Rails](./rails.md)
|
27
|
+
- [Using with other Ruby frameworks](./non_rails.md)
|
28
|
+
- [Using with GraphQL Ruby](./graphql.md)
|
29
|
+
|
23
30
|
## Project State
|
24
31
|
|
25
32
|
The project is being used in production since mid 2018. Major features have been implemented, API has been stabilized. Check out our [development board](https://github.com/palkan/action_policy/projects/1) to see what's coming next.
|
@@ -0,0 +1,364 @@
|
|
1
|
+
(function () {
|
2
|
+
var INDEXS = {};
|
3
|
+
|
4
|
+
var LOCAL_STORAGE = {
|
5
|
+
EXPIRE_KEY: 'docsify.search.expires',
|
6
|
+
INDEX_KEY: 'docsify.search.index'
|
7
|
+
};
|
8
|
+
|
9
|
+
function resolveExpireKey(namespace) {
|
10
|
+
return namespace ? ((LOCAL_STORAGE.EXPIRE_KEY) + "/" + namespace) : LOCAL_STORAGE.EXPIRE_KEY
|
11
|
+
}
|
12
|
+
function resolveIndexKey(namespace) {
|
13
|
+
return namespace ? ((LOCAL_STORAGE.INDEX_KEY) + "/" + namespace) : LOCAL_STORAGE.INDEX_KEY
|
14
|
+
}
|
15
|
+
|
16
|
+
function escapeHtml(string) {
|
17
|
+
var entityMap = {
|
18
|
+
'&': '&',
|
19
|
+
'<': '<',
|
20
|
+
'>': '>',
|
21
|
+
'"': '"',
|
22
|
+
'\'': ''',
|
23
|
+
'/': '/'
|
24
|
+
};
|
25
|
+
|
26
|
+
return String(string).replace(/[&<>"'/]/g, function (s) { return entityMap[s]; })
|
27
|
+
}
|
28
|
+
|
29
|
+
function getAllPaths(router) {
|
30
|
+
var paths = [];
|
31
|
+
|
32
|
+
Docsify.dom.findAll('.sidebar-nav a:not(.section-link):not([data-nosearch])').forEach(function (node) {
|
33
|
+
var href = node.href;
|
34
|
+
var originHref = node.getAttribute('href');
|
35
|
+
var path = router.parse(href).path;
|
36
|
+
|
37
|
+
if (
|
38
|
+
path &&
|
39
|
+
paths.indexOf(path) === -1 &&
|
40
|
+
!Docsify.util.isAbsolutePath(originHref)
|
41
|
+
) {
|
42
|
+
paths.push(path);
|
43
|
+
}
|
44
|
+
});
|
45
|
+
|
46
|
+
return paths
|
47
|
+
}
|
48
|
+
|
49
|
+
function saveData(maxAge, expireKey, indexKey) {
|
50
|
+
localStorage.setItem(expireKey, Date.now() + maxAge);
|
51
|
+
localStorage.setItem(indexKey, JSON.stringify(INDEXS));
|
52
|
+
}
|
53
|
+
|
54
|
+
function genIndex(path, content, router, depth) {
|
55
|
+
if ( content === void 0 ) content = '';
|
56
|
+
|
57
|
+
var tokens = window.marked.lexer(content);
|
58
|
+
var slugify = window.Docsify.slugify;
|
59
|
+
var index = {};
|
60
|
+
var slug;
|
61
|
+
|
62
|
+
tokens.forEach(function (token) {
|
63
|
+
if (token.type === 'heading' && token.depth <= depth) {
|
64
|
+
slug = router.toURL(path, {id: slugify(token.text)});
|
65
|
+
index[slug] = {slug: slug, title: token.text, body: ''};
|
66
|
+
} else {
|
67
|
+
if (!slug) {
|
68
|
+
return
|
69
|
+
}
|
70
|
+
if (!index[slug]) {
|
71
|
+
index[slug] = {slug: slug, title: '', body: ''};
|
72
|
+
} else if (index[slug].body) {
|
73
|
+
index[slug].body += '\n' + (token.text || '');
|
74
|
+
} else {
|
75
|
+
index[slug].body = token.text;
|
76
|
+
}
|
77
|
+
}
|
78
|
+
});
|
79
|
+
slugify.clear();
|
80
|
+
return index
|
81
|
+
}
|
82
|
+
|
83
|
+
/**
|
84
|
+
* @param {String} query
|
85
|
+
* @returns {Array}
|
86
|
+
*/
|
87
|
+
function search(query) {
|
88
|
+
var matchingResults = [];
|
89
|
+
var data = [];
|
90
|
+
Object.keys(INDEXS).forEach(function (key) {
|
91
|
+
data = data.concat(Object.keys(INDEXS[key]).map(function (page) { return INDEXS[key][page]; }));
|
92
|
+
});
|
93
|
+
|
94
|
+
query = query.trim();
|
95
|
+
var keywords = query.split(/[\s\-,\\/]+/);
|
96
|
+
if (keywords.length !== 1) {
|
97
|
+
keywords = [].concat(query, keywords);
|
98
|
+
}
|
99
|
+
|
100
|
+
var loop = function ( i ) {
|
101
|
+
var post = data[i];
|
102
|
+
var matchesScore = 0;
|
103
|
+
var resultStr = '';
|
104
|
+
var postTitle = post.title && post.title.trim();
|
105
|
+
var postContent = post.body && post.body.trim();
|
106
|
+
var postUrl = post.slug || '';
|
107
|
+
|
108
|
+
if (postTitle) {
|
109
|
+
keywords.forEach( function (keyword) {
|
110
|
+
// From https://github.com/sindresorhus/escape-string-regexp
|
111
|
+
var regEx = new RegExp(
|
112
|
+
keyword.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'),
|
113
|
+
'gi'
|
114
|
+
);
|
115
|
+
var indexTitle = -1;
|
116
|
+
var indexContent = -1;
|
117
|
+
|
118
|
+
indexTitle = postTitle ? postTitle.search(regEx) : -1;
|
119
|
+
indexContent = postContent ? postContent.search(regEx) : -1;
|
120
|
+
|
121
|
+
if (indexTitle >= 0 || indexContent >= 0) {
|
122
|
+
matchesScore += indexTitle >= 0 ? 3 : indexContent >= 0 ? 2 : 0;
|
123
|
+
if (indexContent < 0) {
|
124
|
+
indexContent = 0;
|
125
|
+
}
|
126
|
+
|
127
|
+
var start = 0;
|
128
|
+
var end = 0;
|
129
|
+
|
130
|
+
start = indexContent < 11 ? 0 : indexContent - 10;
|
131
|
+
end = start === 0 ? 70 : indexContent + keyword.length + 60;
|
132
|
+
|
133
|
+
if (end > postContent.length) {
|
134
|
+
end = postContent.length;
|
135
|
+
}
|
136
|
+
|
137
|
+
var matchContent =
|
138
|
+
'...' +
|
139
|
+
escapeHtml(postContent)
|
140
|
+
.substring(start, end)
|
141
|
+
.replace(regEx, ("<em class=\"search-keyword\">" + keyword + "</em>")) +
|
142
|
+
'...';
|
143
|
+
|
144
|
+
resultStr += matchContent;
|
145
|
+
}
|
146
|
+
});
|
147
|
+
|
148
|
+
if (matchesScore > 0) {
|
149
|
+
var matchingPost = {
|
150
|
+
title: escapeHtml(postTitle),
|
151
|
+
content: postContent ? resultStr : '',
|
152
|
+
url: postUrl,
|
153
|
+
score: matchesScore
|
154
|
+
};
|
155
|
+
|
156
|
+
matchingResults.push(matchingPost);
|
157
|
+
}
|
158
|
+
}
|
159
|
+
};
|
160
|
+
|
161
|
+
for (var i = 0; i < data.length; i++) loop( i );
|
162
|
+
|
163
|
+
return matchingResults.sort(function (r1, r2) { return r2.score - r1.score; });
|
164
|
+
}
|
165
|
+
|
166
|
+
function init$1(config, vm) {
|
167
|
+
var isAuto = config.paths === 'auto';
|
168
|
+
|
169
|
+
var expireKey = resolveExpireKey(config.namespace);
|
170
|
+
var indexKey = resolveIndexKey(config.namespace);
|
171
|
+
|
172
|
+
var isExpired = localStorage.getItem(expireKey) < Date.now();
|
173
|
+
|
174
|
+
INDEXS = JSON.parse(localStorage.getItem(indexKey));
|
175
|
+
|
176
|
+
if (isExpired) {
|
177
|
+
INDEXS = {};
|
178
|
+
} else if (!isAuto) {
|
179
|
+
return
|
180
|
+
}
|
181
|
+
|
182
|
+
var paths = isAuto ? getAllPaths(vm.router) : config.paths;
|
183
|
+
var len = paths.length;
|
184
|
+
var count = 0;
|
185
|
+
|
186
|
+
paths.forEach(function (path) {
|
187
|
+
if (INDEXS[path]) {
|
188
|
+
return count++
|
189
|
+
}
|
190
|
+
|
191
|
+
Docsify
|
192
|
+
.get(vm.router.getFile(path), false, vm.config.requestHeaders)
|
193
|
+
.then(function (result) {
|
194
|
+
INDEXS[path] = genIndex(path, result, vm.router, config.depth);
|
195
|
+
len === ++count && saveData(config.maxAge, expireKey, indexKey);
|
196
|
+
});
|
197
|
+
});
|
198
|
+
}
|
199
|
+
|
200
|
+
var NO_DATA_TEXT = '';
|
201
|
+
var options;
|
202
|
+
|
203
|
+
function style() {
|
204
|
+
var code = "\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .input-wrap {\n display: flex;\n align-items: center;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 0 7px;\n line-height: 36px;\n font-size: 14px;\n}\n\n.search input::-webkit-search-decoration,\n.search input::-webkit-search-cancel-button,\n.search input {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n.search .clear-button {\n width: 36px;\n text-align: right;\n display: none;\n}\n\n.search .clear-button.show {\n display: block;\n}\n\n.search .clear-button svg {\n transform: scale(.5);\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}\n\n.app-name.hide, .sidebar-nav.hide {\n display: none;\n}";
|
205
|
+
|
206
|
+
Docsify.dom.style(code);
|
207
|
+
}
|
208
|
+
|
209
|
+
function tpl(defaultValue) {
|
210
|
+
if ( defaultValue === void 0 ) defaultValue = '';
|
211
|
+
|
212
|
+
var html =
|
213
|
+
"<div class=\"input-wrap\">\n <input type=\"search\" value=\"" + defaultValue + "\" />\n <div class=\"clear-button\">\n <svg width=\"26\" height=\"24\">\n <circle cx=\"12\" cy=\"12\" r=\"11\" fill=\"#ccc\" />\n <path stroke=\"white\" stroke-width=\"2\" d=\"M8.25,8.25,15.75,15.75\" />\n <path stroke=\"white\" stroke-width=\"2\"d=\"M8.25,15.75,15.75,8.25\" />\n </svg>\n </div>\n </div>\n <div class=\"results-panel\"></div>\n </div>";
|
214
|
+
var el = Docsify.dom.create('div', html);
|
215
|
+
var aside = Docsify.dom.find('aside');
|
216
|
+
|
217
|
+
Docsify.dom.toggleClass(el, 'search');
|
218
|
+
Docsify.dom.before(aside, el);
|
219
|
+
}
|
220
|
+
|
221
|
+
function doSearch(value) {
|
222
|
+
var $search = Docsify.dom.find('div.search');
|
223
|
+
var $panel = Docsify.dom.find($search, '.results-panel');
|
224
|
+
var $clearBtn = Docsify.dom.find($search, '.clear-button');
|
225
|
+
var $sidebarNav = Docsify.dom.find('.sidebar-nav');
|
226
|
+
var $appName = Docsify.dom.find('.app-name');
|
227
|
+
|
228
|
+
if (!value) {
|
229
|
+
$panel.classList.remove('show');
|
230
|
+
$clearBtn.classList.remove('show');
|
231
|
+
$panel.innerHTML = '';
|
232
|
+
|
233
|
+
if (options.hideOtherSidebarContent) {
|
234
|
+
$sidebarNav.classList.remove('hide');
|
235
|
+
$appName.classList.remove('hide');
|
236
|
+
}
|
237
|
+
return
|
238
|
+
}
|
239
|
+
var matchs = search(value);
|
240
|
+
|
241
|
+
var html = '';
|
242
|
+
matchs.forEach(function (post) {
|
243
|
+
html += "<div class=\"matching-post\">\n<a href=\"" + (post.url) + "\">\n<h2>" + (post.title) + "</h2>\n<p>" + (post.content) + "</p>\n</a>\n</div>";
|
244
|
+
});
|
245
|
+
|
246
|
+
$panel.classList.add('show');
|
247
|
+
$clearBtn.classList.add('show');
|
248
|
+
$panel.innerHTML = html || ("<p class=\"empty\">" + NO_DATA_TEXT + "</p>");
|
249
|
+
if (options.hideOtherSidebarContent) {
|
250
|
+
$sidebarNav.classList.add('hide');
|
251
|
+
$appName.classList.add('hide');
|
252
|
+
}
|
253
|
+
}
|
254
|
+
|
255
|
+
function bindEvents() {
|
256
|
+
var $search = Docsify.dom.find('div.search');
|
257
|
+
var $input = Docsify.dom.find($search, 'input');
|
258
|
+
var $inputWrap = Docsify.dom.find($search, '.input-wrap');
|
259
|
+
|
260
|
+
var timeId;
|
261
|
+
// Prevent to Fold sidebar
|
262
|
+
Docsify.dom.on(
|
263
|
+
$search,
|
264
|
+
'click',
|
265
|
+
function (e) { return e.target.tagName !== 'A' && e.stopPropagation(); }
|
266
|
+
);
|
267
|
+
Docsify.dom.on($input, 'input', function (e) {
|
268
|
+
clearTimeout(timeId);
|
269
|
+
timeId = setTimeout(function (_) { return doSearch(e.target.value.trim()); }, 100);
|
270
|
+
});
|
271
|
+
Docsify.dom.on($inputWrap, 'click', function (e) {
|
272
|
+
// Click input outside
|
273
|
+
if (e.target.tagName !== 'INPUT') {
|
274
|
+
$input.value = '';
|
275
|
+
doSearch();
|
276
|
+
}
|
277
|
+
});
|
278
|
+
}
|
279
|
+
|
280
|
+
function updatePlaceholder(text, path) {
|
281
|
+
var $input = Docsify.dom.getNode('.search input[type="search"]');
|
282
|
+
|
283
|
+
if (!$input) {
|
284
|
+
return
|
285
|
+
}
|
286
|
+
if (typeof text === 'string') {
|
287
|
+
$input.placeholder = text;
|
288
|
+
} else {
|
289
|
+
var match = Object.keys(text).filter(function (key) { return path.indexOf(key) > -1; })[0];
|
290
|
+
$input.placeholder = text[match];
|
291
|
+
}
|
292
|
+
}
|
293
|
+
|
294
|
+
function updateNoData(text, path) {
|
295
|
+
if (typeof text === 'string') {
|
296
|
+
NO_DATA_TEXT = text;
|
297
|
+
} else {
|
298
|
+
var match = Object.keys(text).filter(function (key) { return path.indexOf(key) > -1; })[0];
|
299
|
+
NO_DATA_TEXT = text[match];
|
300
|
+
}
|
301
|
+
}
|
302
|
+
|
303
|
+
function updateOptions(opts) {
|
304
|
+
options = opts;
|
305
|
+
}
|
306
|
+
|
307
|
+
function init(opts, vm) {
|
308
|
+
var keywords = vm.router.parse().query.s;
|
309
|
+
|
310
|
+
updateOptions(opts);
|
311
|
+
style();
|
312
|
+
tpl(keywords);
|
313
|
+
bindEvents();
|
314
|
+
keywords && setTimeout(function (_) { return doSearch(keywords); }, 500);
|
315
|
+
}
|
316
|
+
|
317
|
+
function update(opts, vm) {
|
318
|
+
updateOptions(opts);
|
319
|
+
updatePlaceholder(opts.placeholder, vm.route.path);
|
320
|
+
updateNoData(opts.noData, vm.route.path);
|
321
|
+
}
|
322
|
+
|
323
|
+
var CONFIG = {
|
324
|
+
placeholder: 'Type to search',
|
325
|
+
noData: 'No Results!',
|
326
|
+
paths: 'auto',
|
327
|
+
depth: 2,
|
328
|
+
maxAge: 86400000, // 1 day
|
329
|
+
hideOtherSidebarContent: false,
|
330
|
+
namespace: undefined
|
331
|
+
};
|
332
|
+
|
333
|
+
var install = function (hook, vm) {
|
334
|
+
var util = Docsify.util;
|
335
|
+
var opts = vm.config.search || CONFIG;
|
336
|
+
|
337
|
+
if (Array.isArray(opts)) {
|
338
|
+
CONFIG.paths = opts;
|
339
|
+
} else if (typeof opts === 'object') {
|
340
|
+
CONFIG.paths = Array.isArray(opts.paths) ? opts.paths : 'auto';
|
341
|
+
CONFIG.maxAge = util.isPrimitive(opts.maxAge) ? opts.maxAge : CONFIG.maxAge;
|
342
|
+
CONFIG.placeholder = opts.placeholder || CONFIG.placeholder;
|
343
|
+
CONFIG.noData = opts.noData || CONFIG.noData;
|
344
|
+
CONFIG.depth = opts.depth || CONFIG.depth;
|
345
|
+
CONFIG.hideOtherSidebarContent = opts.hideOtherSidebarContent || CONFIG.hideOtherSidebarContent;
|
346
|
+
CONFIG.namespace = opts.namespace || CONFIG.namespace;
|
347
|
+
}
|
348
|
+
|
349
|
+
var isAuto = CONFIG.paths === 'auto';
|
350
|
+
|
351
|
+
hook.mounted(function (_) {
|
352
|
+
init(CONFIG, vm);
|
353
|
+
!isAuto && init$1(CONFIG, vm);
|
354
|
+
});
|
355
|
+
hook.doneEach(function (_) {
|
356
|
+
update(CONFIG, vm);
|
357
|
+
isAuto && init$1(CONFIG, vm);
|
358
|
+
});
|
359
|
+
};
|
360
|
+
|
361
|
+
$docsify.plugins = [].concat(install, $docsify.plugins);
|
362
|
+
|
363
|
+
}());
|
364
|
+
|
data/docs/assets/styles.css
CHANGED
@@ -56,18 +56,23 @@ a {
|
|
56
56
|
text-align: center;
|
57
57
|
}
|
58
58
|
|
59
|
-
|
60
|
-
aside.sidebar {
|
59
|
+
.sidebar {
|
61
60
|
border: none;
|
62
61
|
background-color: var(--theme-color-secondary);
|
63
62
|
color: #fff;
|
64
63
|
width: 20%;
|
64
|
+
display: flex;
|
65
|
+
flex-direction: column;
|
65
66
|
}
|
66
67
|
|
67
68
|
body.close .sidebar {
|
68
69
|
transform: translateX(-100%);
|
69
70
|
}
|
70
71
|
|
72
|
+
.sidebar .sidebar-nav {
|
73
|
+
order: 3;
|
74
|
+
}
|
75
|
+
|
71
76
|
.sidebar ul li a {
|
72
77
|
color: rgba(255,255,255,0.8);
|
73
78
|
color: var(--theme-color-light);
|
@@ -89,7 +94,32 @@ body.close .sidebar {
|
|
89
94
|
}
|
90
95
|
|
91
96
|
.sidebar > h1 {
|
97
|
+
order: 1;
|
92
98
|
font-size: 2rem;
|
99
|
+
margin-top: 1.5rem;
|
100
|
+
}
|
101
|
+
|
102
|
+
/* search plugin related */
|
103
|
+
|
104
|
+
.sidebar .search {
|
105
|
+
order: 2;
|
106
|
+
margin: 1.5rem 0 0 0;
|
107
|
+
border: none;
|
108
|
+
margin-top: 20px;
|
109
|
+
margin-bottom: 20px;
|
110
|
+
padding: 6px;
|
111
|
+
}
|
112
|
+
|
113
|
+
.sidebar .search .input-wrap {
|
114
|
+
border: none;
|
115
|
+
position: relative;
|
116
|
+
}
|
117
|
+
|
118
|
+
.sidebar .search .clear-button.show {
|
119
|
+
display: block;
|
120
|
+
position: absolute;
|
121
|
+
right: 4px;
|
122
|
+
top: 8px;
|
93
123
|
}
|
94
124
|
|
95
125
|
body .sidebar-toggle {
|
data/docs/custom_policy.md
CHANGED
@@ -12,6 +12,7 @@ class ActionPolicy::Base
|
|
12
12
|
include ActionPolicy::Policy::PreCheck
|
13
13
|
include ActionPolicy::Policy::Reasons
|
14
14
|
include ActionPolicy::Policy::Aliases
|
15
|
+
include ActionPolicy::Policy::Scoping
|
15
16
|
include ActionPolicy::Policy::Cache
|
16
17
|
include ActionPolicy::Policy::CachedApply
|
17
18
|
include ActionPolicy::Policy::Defaults
|
data/docs/decorators.md
CHANGED
@@ -15,7 +15,7 @@ module ActionPolicy
|
|
15
15
|
module Draper
|
16
16
|
def policy_for(record:, **opts)
|
17
17
|
# From https://github.com/GoodMeasuresLLC/draper-cancancan/blob/master/lib/draper/cancancan.rb
|
18
|
-
record = record.model while record.is_a?(Draper::Decorator)
|
18
|
+
record = record.model while record.is_a?(::Draper::Decorator)
|
19
19
|
super(record: record, **opts)
|
20
20
|
end
|
21
21
|
end
|
data/docs/index.html
CHANGED
@@ -30,10 +30,14 @@
|
|
30
30
|
loadSidebar: true,
|
31
31
|
subMaxLevel: 3,
|
32
32
|
ga: 'UA-104346673-3',
|
33
|
-
auto2top:true
|
33
|
+
auto2top: true,
|
34
|
+
search: {
|
35
|
+
namespace: 'action-policy'
|
36
|
+
}
|
34
37
|
}
|
35
38
|
</script>
|
36
39
|
<script src="assets/docsify.min.js"></script>
|
40
|
+
<script src="assets/docsify-search.js"></script>
|
37
41
|
<script src="assets/prism-ruby.min.js"></script>
|
38
42
|
</body>
|
39
43
|
</html>
|
data/lib/action_policy.rb
CHANGED
@@ -13,7 +13,10 @@ module ActionPolicy
|
|
13
13
|
|
14
14
|
def initialize(target, message = nil)
|
15
15
|
@target = target
|
16
|
-
@message =
|
16
|
+
@message =
|
17
|
+
message ||
|
18
|
+
"Couldn't find policy class for #{target.inspect}" \
|
19
|
+
"#{target.is_a?(Module) ? "" : " (#{target.class})"}"
|
17
20
|
end
|
18
21
|
end
|
19
22
|
|
@@ -124,15 +124,14 @@ module ActionPolicy
|
|
124
124
|
def scoping_handlers
|
125
125
|
return @scoping_handlers if instance_variable_defined?(:@scoping_handlers)
|
126
126
|
|
127
|
-
@scoping_handlers =
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
127
|
+
@scoping_handlers =
|
128
|
+
Hash.new { |h, k| h[k] = {} }.tap do |handlers|
|
129
|
+
if superclass.respond_to?(:scoping_handlers)
|
130
|
+
superclass.scoping_handlers.each do |k, v|
|
131
|
+
handlers[k] = v.dup
|
132
|
+
end
|
133
|
+
end
|
132
134
|
end
|
133
|
-
end
|
134
|
-
|
135
|
-
@scoping_handlers
|
136
135
|
end
|
137
136
|
|
138
137
|
# Define scope type matcher.
|
@@ -149,11 +148,12 @@ module ActionPolicy
|
|
149
148
|
def scope_matchers
|
150
149
|
return @scope_matchers if instance_variable_defined?(:@scope_matchers)
|
151
150
|
|
152
|
-
@scope_matchers =
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
151
|
+
@scope_matchers =
|
152
|
+
if superclass.respond_to?(:scope_matchers)
|
153
|
+
superclass.scope_matchers.dup
|
154
|
+
else
|
155
|
+
[]
|
156
|
+
end
|
157
157
|
end
|
158
158
|
end
|
159
159
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: action_policy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vladimir Dementyev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-06-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -165,6 +165,7 @@ files:
|
|
165
165
|
- docs/README.md
|
166
166
|
- docs/_sidebar.md
|
167
167
|
- docs/aliases.md
|
168
|
+
- docs/assets/docsify-search.js
|
168
169
|
- docs/assets/docsify.min.js
|
169
170
|
- docs/assets/fonts/FiraCode-Medium.woff
|
170
171
|
- docs/assets/fonts/FiraCode-Regular.woff
|