excom 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7f560f7894a0530703e6a5ecb6f472e61b4ae37b
4
- data.tar.gz: 764f807e1ccbb3f3daf38dfda5cca89b42a7b22c
3
+ metadata.gz: cf41e297a57b63652c79c3455846ceef0398f1ea
4
+ data.tar.gz: 27447ccbbcee3505e65f4a7d78908437f7197d6a
5
5
  SHA512:
6
- metadata.gz: bf2e04d3f7f91341768d54ce4033041ba3787529e45390beaf4035e2d72bfed4c5454e6fecab3c00cc6ff9f583cc8da0b19863a970a0ad941467ef464cc42670
7
- data.tar.gz: 06d5a6496d1a653ac920b51f9712af6a4321235a41624c18931c46170936b953a847ad3e014660df6356f89368ffd2dd470269478d59a6386cf35d20b4d47b27
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 provide Sentry classes
190
- for commands that use this plugin. Each Sentry class hosts logic responsible for allowing or denying
191
- corresponding command's execution or related checks. Much like [pundit](https://github.com/elabs/pundit)
192
- Policies, but more. Where pundit governs only authorization logic, Excom's Sentries can deny execution
193
- with any reason you find appropriate.
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
- class Posts::DestroySentry < Excom::Sentry
207
- delegate :post, :context, to: :command
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
- # disallow to destroy posts that are older than 1 hour
218
- (post.created_at + 1.hour).past?
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
- class Command < Excom::Command
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
- class MySentry < Excom::Sentry
27
- deny_with :unauthorized
28
-
29
- def execute?
30
- command.foo != 5
47
+ def threshold
48
+ 0
31
49
  end
32
50
 
33
- deny_with :unprocessable_entity do
34
- def bar?
35
- command.bar && command.bar > 0
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 method_missing(name, *)
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)
@@ -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
- if _sentry_class.is_a?(String)
47
- return _sentry_class.constantize if _sentry_class.respond_to?(:constantize)
51
+ @sentry_class =
52
+ if _sentry_class.is_a?(String)
53
+ return _sentry_class.constantize if _sentry_class.respond_to?(:constantize)
48
54
 
49
- names = _sentry_class.split('::'.freeze)
50
- names.shift if names.first.empty?
51
- names.reduce(Object){ |obj, name| obj.const_get(name) }
52
- else
53
- _sentry_class
54
- end
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
@@ -1,3 +1,3 @@
1
1
  module Excom
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
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.2.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-01-14 00:00:00.000000000 Z
11
+ date: 2018-02-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler