excom 0.2.0 → 0.3.0
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/README.md +17 -17
- data/bin/console +39 -11
- data/lib/excom/plugins/pluggable.rb +9 -0
- data/lib/excom/plugins/sentry/sentinel.rb +63 -8
- data/lib/excom/plugins/sentry.rb +29 -8
- data/lib/excom/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cf41e297a57b63652c79c3455846ceef0398f1ea
|
4
|
+
data.tar.gz: 27447ccbbcee3505e65f4a7d78908437f7197d6a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3cbfb293d339f11984aab4b78f86ad1b93f70ba0721dbabcaecd4e175a8ec56bc8ffb583fcf88b2256cd85abc83f1e386c9ec5015acb4e6b16a5eff4437689d8
|
7
|
+
data.tar.gz: 994147da0552e012f35bed28e7c19933f06ea1a88e28cef013d8869a8a9d4cbf9bd1b7d3b59c86584ea9b6c62bf3a698bfd8c03f83d63cc6c3267e728ef93e8e
|
data/README.md
CHANGED
@@ -186,36 +186,36 @@ class Posts::Archive < Excom::Command
|
|
186
186
|
end
|
187
187
|
```
|
188
188
|
|
189
|
-
- [`:sentry`](https://github.com/akuzko/excom/wiki/Plugins#sentry) - Allows you to
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
189
|
+
- [`:sentry`](https://github.com/akuzko/excom/wiki/Plugins#sentry) - Allows you to define sentry logic that
|
190
|
+
will allow or deny command's execution or other related checks. This logic can be defined inline in command
|
191
|
+
classes or in dedicated Sentry classes. Much like [pundit](https://github.com/elabs/pundit) Policies, but
|
192
|
+
more. Where pundit governs only authorization logic, Excom's Sentries can deny execution with any reason
|
193
|
+
you find appropriate.
|
194
194
|
|
195
195
|
```rb
|
196
196
|
class Posts::Destroy < Excom::Command
|
197
197
|
use :context
|
198
198
|
use :sentry
|
199
|
+
|
199
200
|
args :post
|
200
201
|
|
201
202
|
def run
|
202
203
|
post.destroy
|
203
204
|
end
|
204
|
-
end
|
205
205
|
|
206
|
-
|
207
|
-
|
208
|
-
deny_with :unauthorized
|
209
|
-
|
210
|
-
def execute?
|
211
|
-
# only author can destroy a post
|
212
|
-
post.author_id == context[:current_user].id
|
213
|
-
end
|
206
|
+
sentry delegate: [:context] do
|
207
|
+
deny_with :unauthorized
|
214
208
|
|
215
|
-
deny_with :unprocessable_entity do
|
216
209
|
def execute?
|
217
|
-
#
|
218
|
-
|
210
|
+
# only author can destroy a post
|
211
|
+
post.author_id == context[:current_user].id
|
212
|
+
end
|
213
|
+
|
214
|
+
deny_with :unprocessable_entity do
|
215
|
+
def execute?
|
216
|
+
# disallow to destroy posts that are older than 1 hour
|
217
|
+
(post.created_at + 1.hour).past?
|
218
|
+
end
|
219
219
|
end
|
220
220
|
end
|
221
221
|
end
|
data/bin/console
CHANGED
@@ -3,11 +3,33 @@
|
|
3
3
|
require "bundler/setup"
|
4
4
|
require "excom"
|
5
5
|
|
6
|
-
|
6
|
+
Excom::Sentry.deny_with :unauthorized
|
7
|
+
|
8
|
+
class Show < Excom::Command
|
7
9
|
use :sentry, class: 'MySentry'
|
8
|
-
use :assertions
|
9
10
|
use :caching
|
10
11
|
|
12
|
+
args :foo
|
13
|
+
|
14
|
+
def run
|
15
|
+
{foo: foo}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class MySentry < Excom::Sentry
|
20
|
+
def execute?
|
21
|
+
foo != 5
|
22
|
+
end
|
23
|
+
|
24
|
+
def save?
|
25
|
+
sentry(:save).execute?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Save < Excom::Command
|
30
|
+
use :sentry, delegate: [:threshold]
|
31
|
+
use :assertions
|
32
|
+
|
11
33
|
args :foo
|
12
34
|
opts :bar, :baz
|
13
35
|
|
@@ -21,18 +43,24 @@ class Command < Excom::Command
|
|
21
43
|
result ok: foo * 2
|
22
44
|
assert { foo > bar }
|
23
45
|
end
|
24
|
-
end
|
25
46
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
def execute?
|
30
|
-
command.foo != 5
|
47
|
+
def threshold
|
48
|
+
0
|
31
49
|
end
|
32
50
|
|
33
|
-
|
34
|
-
def
|
35
|
-
|
51
|
+
sentry do
|
52
|
+
def execute?
|
53
|
+
foo != 6
|
54
|
+
end
|
55
|
+
|
56
|
+
deny_with :unprocessable_entity do
|
57
|
+
def execute?
|
58
|
+
foo > 0
|
59
|
+
end
|
60
|
+
|
61
|
+
def bar?
|
62
|
+
bar && bar > threshold
|
63
|
+
end
|
36
64
|
end
|
37
65
|
end
|
38
66
|
end
|
@@ -14,7 +14,16 @@ module Excom
|
|
14
14
|
extension.used(self, **opts)
|
15
15
|
end
|
16
16
|
|
17
|
+
plugins[name] = Reflection.new(extension, opts)
|
18
|
+
|
17
19
|
extension
|
18
20
|
end
|
21
|
+
|
22
|
+
def plugins
|
23
|
+
@plugins ||= {}
|
24
|
+
end
|
25
|
+
alias :extensions :plugins
|
26
|
+
|
27
|
+
Reflection = Struct.new(:extension, :options)
|
19
28
|
end
|
20
29
|
end
|
@@ -1,6 +1,23 @@
|
|
1
1
|
module Excom
|
2
2
|
module Plugins::Sentry
|
3
3
|
class Sentinel
|
4
|
+
def self.inherited(sentry_class)
|
5
|
+
return unless self == Sentinel
|
6
|
+
|
7
|
+
sentry_class.denial_reason = denial_reason
|
8
|
+
sentry_class.const_set(:Delegations, Module.new)
|
9
|
+
sentry_class.send(:include, sentry_class::Delegations)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.command_class=(klass)
|
13
|
+
sentinels.each{ |s| s.command_class = klass }
|
14
|
+
@command_class = klass
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.command_class
|
18
|
+
@command_class
|
19
|
+
end
|
20
|
+
|
4
21
|
def self.deny_with(reason)
|
5
22
|
return self.denial_reason = reason unless block_given?
|
6
23
|
|
@@ -35,6 +52,10 @@ module Excom
|
|
35
52
|
@sentinels ||= []
|
36
53
|
end
|
37
54
|
|
55
|
+
def self.delegations
|
56
|
+
const_get(:Delegations)
|
57
|
+
end
|
58
|
+
|
38
59
|
attr_reader :command
|
39
60
|
|
40
61
|
def initialize(command)
|
@@ -52,13 +73,7 @@ module Excom
|
|
52
73
|
end
|
53
74
|
|
54
75
|
def sentry(klass)
|
55
|
-
unless Class === klass
|
56
|
-
klass_name = self.class.name.sub(/[^:]+\Z/, ''.freeze) + "_#{klass}".gsub!(/(_([a-z]))/){ $2.upcase } + 'Sentry'.freeze
|
57
|
-
klass = klass_name.respond_to?(:constantize) ?
|
58
|
-
klass_name.constantize :
|
59
|
-
klass_name.split('::'.freeze).reduce(Object){ |obj, name| obj.const_get(name) }
|
60
|
-
end
|
61
|
-
|
76
|
+
klass = derive_sentry_class(klass) unless Class === klass
|
62
77
|
klass.new(command)
|
63
78
|
end
|
64
79
|
|
@@ -82,7 +97,47 @@ module Excom
|
|
82
97
|
end
|
83
98
|
end
|
84
99
|
|
85
|
-
def
|
100
|
+
private def derive_sentry_class(klass)
|
101
|
+
constantize(klass, '::Sentry'.freeze)
|
102
|
+
rescue NameError
|
103
|
+
constantize(klass, 'Sentry'.freeze)
|
104
|
+
end
|
105
|
+
|
106
|
+
private def constantize(klass, sentry_name)
|
107
|
+
module_prefix = (inline? ? self.class.command_class.name : self.class.name).sub(/[^:]+\Z/, ''.freeze)
|
108
|
+
|
109
|
+
klass_name = module_prefix + "_#{klass}".gsub!(/(_([a-z]))/){ $2.upcase } + sentry_name
|
110
|
+
|
111
|
+
klass_name.respond_to?(:constantize) ?
|
112
|
+
klass_name.constantize :
|
113
|
+
klass_name.split('::'.freeze).reduce(Object){ |obj, name| obj.const_get(name) }
|
114
|
+
end
|
115
|
+
|
116
|
+
private def inline?
|
117
|
+
self.class.command_class.const_defined?(:Sentry) && self.class.command_class::Sentry == self.class
|
118
|
+
end
|
119
|
+
|
120
|
+
private def define_delegations!
|
121
|
+
delegated_methods = self.class.command_class.arg_methods.instance_methods +
|
122
|
+
Array(self.class.command_class.plugins[:sentry].options[:delegate])
|
123
|
+
|
124
|
+
delegated_methods.each do |name|
|
125
|
+
self.class.delegations.send(:define_method, name) { command.public_send(name) }
|
126
|
+
end
|
127
|
+
|
128
|
+
self.class.instance_variable_set('@delegations_defined'.freeze, true)
|
129
|
+
end
|
130
|
+
|
131
|
+
private def delegations_defined?
|
132
|
+
self.class.instance_variable_get('@delegations_defined'.freeze)
|
133
|
+
end
|
134
|
+
|
135
|
+
def method_missing(name, *args)
|
136
|
+
unless delegations_defined?
|
137
|
+
define_delegations!
|
138
|
+
return send(name, *args) if respond_to?(name)
|
139
|
+
end
|
140
|
+
|
86
141
|
if name.to_s.end_with?(??)
|
87
142
|
sentinels[1..-1].each do |sentry|
|
88
143
|
return sentry.public_send(name) if sentry.respond_to?(name)
|
data/lib/excom/plugins/sentry.rb
CHANGED
@@ -32,6 +32,10 @@ module Excom
|
|
32
32
|
@sentry ||= self.class.sentry_class.new(self)
|
33
33
|
end
|
34
34
|
|
35
|
+
def sentry_hash
|
36
|
+
sentry.to_hash
|
37
|
+
end
|
38
|
+
|
35
39
|
module ClassMethods
|
36
40
|
attr_writer :_sentry_class
|
37
41
|
|
@@ -42,21 +46,38 @@ module Excom
|
|
42
46
|
|
43
47
|
def sentry_class(klass = nil)
|
44
48
|
return self._sentry_class = klass unless klass.nil?
|
49
|
+
return @sentry_class if defined? @sentry_class
|
45
50
|
|
46
|
-
|
47
|
-
|
51
|
+
@sentry_class =
|
52
|
+
if _sentry_class.is_a?(String)
|
53
|
+
return _sentry_class.constantize if _sentry_class.respond_to?(:constantize)
|
48
54
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
+
names = _sentry_class.split('::'.freeze)
|
56
|
+
names.shift if names.first.empty?
|
57
|
+
names.reduce(Object){ |obj, name| obj.const_get(name) }
|
58
|
+
else
|
59
|
+
_sentry_class
|
60
|
+
end
|
61
|
+
|
62
|
+
@sentry_class.command_class = self
|
63
|
+
@sentry_class
|
55
64
|
end
|
56
65
|
|
57
66
|
def _sentry_class
|
58
67
|
@_sentry_class ||= "#{name}Sentry"
|
59
68
|
end
|
69
|
+
|
70
|
+
def sentry(delegate: [], &block)
|
71
|
+
(plugins[:sentry].options[:delegate] ||= []).concat(delegate).uniq!
|
72
|
+
|
73
|
+
if const_defined?(:Sentry)
|
74
|
+
const_get(:Sentry).class_eval(&block)
|
75
|
+
else
|
76
|
+
@_sentry_class = @sentry_class = Class.new(Sentry, &block)
|
77
|
+
@sentry_class.command_class = self
|
78
|
+
const_set(:Sentry, @_sentry_class)
|
79
|
+
end
|
80
|
+
end
|
60
81
|
end
|
61
82
|
end
|
62
83
|
end
|
data/lib/excom/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: excom
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Artem Kuzko
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-02-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|