pragma-operation 1.4.0 → 1.5.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/.rubocop.yml +8 -1
- data/Gemfile +2 -0
- data/doc/02-contracts.md +23 -0
- data/doc/03-policies.md +23 -0
- data/doc/04-decorators.md +26 -2
- data/lib/pragma/operation/authorization.rb +28 -20
- data/lib/pragma/operation/decoration.rb +23 -19
- data/lib/pragma/operation/validation.rb +26 -21
- data/lib/pragma/operation/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0d6cc18cf80b23f1275cdea3bdbdb5f898ab18bc
|
4
|
+
data.tar.gz: 432bbd348981310bff295afb60c4e188d6767f4d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 34357046619e0302eacf52f6143c72eab02324a8ce781eb1f400afcb315f06cba2ef9aa469c67a68174fb117c8d281410458c27cc078f909c57a4f4768918a1d
|
7
|
+
data.tar.gz: 530f950bb5901d6436edb3954ab4553483b6f4375c8f37fa36dfe5805e2595dca6885295c7d55aa3f130e911932f08ca999bebafd87e71b36ac9b5a726604312
|
data/.rubocop.yml
CHANGED
@@ -53,7 +53,7 @@ Style/BracesAroundHashParameters:
|
|
53
53
|
EnforcedStyle: context_dependent
|
54
54
|
|
55
55
|
Lint/EndAlignment:
|
56
|
-
|
56
|
+
EnforcedStyleAlignWith: variable
|
57
57
|
AutoCorrect: true
|
58
58
|
|
59
59
|
Style/AndOr:
|
@@ -85,3 +85,10 @@ Metrics/CyclomaticComplexity:
|
|
85
85
|
|
86
86
|
Metrics/ClassLength:
|
87
87
|
Enabled: false
|
88
|
+
|
89
|
+
Style/CaseIndentation:
|
90
|
+
EnforcedStyle: end
|
91
|
+
IndentOneStep: false
|
92
|
+
|
93
|
+
Metrics/BlockLength:
|
94
|
+
Enabled: false
|
data/Gemfile
CHANGED
data/doc/02-contracts.md
CHANGED
@@ -27,6 +27,29 @@ module API
|
|
27
27
|
end
|
28
28
|
```
|
29
29
|
|
30
|
+
You can also pass a block to compute the contract class dynamically. If the block returns `nil`,
|
31
|
+
validation will be skipped:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
module API
|
35
|
+
module V1
|
36
|
+
module Post
|
37
|
+
module Operation
|
38
|
+
class Create < Pragma::Operation::Base
|
39
|
+
contract do |context|
|
40
|
+
# ...
|
41
|
+
end
|
42
|
+
|
43
|
+
def call
|
44
|
+
# ...
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
30
53
|
If the contract passes validation, then all is good. If not, an error is raised:
|
31
54
|
|
32
55
|
```ruby
|
data/doc/03-policies.md
CHANGED
@@ -26,6 +26,29 @@ module API
|
|
26
26
|
end
|
27
27
|
```
|
28
28
|
|
29
|
+
You can also pass a block to compute the policy class dynamically. If the block returns `nil`,
|
30
|
+
authorization will be skipped:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
module API
|
34
|
+
module V1
|
35
|
+
module Post
|
36
|
+
module Operation
|
37
|
+
class Create < Pragma::Operation::Base
|
38
|
+
policy do |context|
|
39
|
+
# ...
|
40
|
+
end
|
41
|
+
|
42
|
+
def call
|
43
|
+
# ...
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
```
|
51
|
+
|
29
52
|
Of course, you will now have to pass the user performing the operation in addition to the usual
|
30
53
|
parameters:
|
31
54
|
|
data/doc/04-decorators.md
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
# Decorators
|
2
2
|
|
3
3
|
Operations integrate with [Pragma::Decorator](https://github.com/pragmarb/pragma-decorator). All you
|
4
|
-
have to do is specify the
|
4
|
+
have to do is specify the decorator class with `#decorator`. This will give you access to
|
5
|
+
`#decorate`:
|
5
6
|
|
6
7
|
```ruby
|
7
8
|
module API
|
@@ -9,7 +10,7 @@ module API
|
|
9
10
|
module Post
|
10
11
|
module Operation
|
11
12
|
class Show < Pragma::Operation::Base
|
12
|
-
|
13
|
+
decorator API::V1::Post::Decorator
|
13
14
|
|
14
15
|
def call
|
15
16
|
post = Post.find(params[:id])
|
@@ -22,5 +23,28 @@ module API
|
|
22
23
|
end
|
23
24
|
```
|
24
25
|
|
26
|
+
You can also pass a block to compute the decorator class dynamically. If the block returns `nil`,
|
27
|
+
decoration will be skipped:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
module API
|
31
|
+
module V1
|
32
|
+
module Post
|
33
|
+
module Operation
|
34
|
+
class Show < Pragma::Operation::Base
|
35
|
+
decorator do |context|
|
36
|
+
# ...
|
37
|
+
end
|
38
|
+
|
39
|
+
def call
|
40
|
+
# ...
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
25
49
|
Note that `#decorate` works with both singular resources and collections, as it uses the decorator's
|
26
50
|
[`.represent`](http://trailblazer.to/gems/representable/3.0/api.html) method.
|
@@ -14,8 +14,15 @@ module Pragma
|
|
14
14
|
# Sets the policy to use for authorizing this operation.
|
15
15
|
#
|
16
16
|
# @param klass [Class] a subclass of +Pragma::Policy::Base+
|
17
|
-
|
18
|
-
|
17
|
+
#
|
18
|
+
# @yield A block which will be called with the operation's context which should return
|
19
|
+
# the policy class. The block can also return +nil+ if authorization should be skipped.
|
20
|
+
def policy(klass = nil, &block)
|
21
|
+
if !klass && !block_given?
|
22
|
+
fail ArgumentError, 'You must pass either a policy class or a block'
|
23
|
+
end
|
24
|
+
|
25
|
+
@policy = klass || block
|
19
26
|
end
|
20
27
|
|
21
28
|
# Returns the policy class.
|
@@ -24,19 +31,6 @@ module Pragma
|
|
24
31
|
def policy_klass
|
25
32
|
@policy
|
26
33
|
end
|
27
|
-
|
28
|
-
# Builds the policy for the given user and resource, using the previous defined policy
|
29
|
-
# class.
|
30
|
-
#
|
31
|
-
# @param user [Object]
|
32
|
-
# @param resource [Object]
|
33
|
-
#
|
34
|
-
# @return [Pragma::Policy::Base]
|
35
|
-
#
|
36
|
-
# @see #policy
|
37
|
-
def build_policy(user:, resource:)
|
38
|
-
policy_klass.new(user: user, resource: resource)
|
39
|
-
end
|
40
34
|
end
|
41
35
|
|
42
36
|
module InstanceMethods # :nodoc:
|
@@ -50,7 +44,10 @@ module Pragma
|
|
50
44
|
# @see .policy
|
51
45
|
# @see .build_policy
|
52
46
|
def build_policy(resource)
|
53
|
-
|
47
|
+
policy_klass = compute_policy_klass
|
48
|
+
return resource unless policy_klass
|
49
|
+
|
50
|
+
policy_klass.new(user: current_user, resource: resource)
|
54
51
|
end
|
55
52
|
|
56
53
|
# Authorizes this operation on the provided resource or policy.
|
@@ -61,13 +58,15 @@ module Pragma
|
|
61
58
|
#
|
62
59
|
# @return [Boolean] whether the operation is authorized
|
63
60
|
def authorize(authorizable)
|
64
|
-
return true unless
|
61
|
+
return true unless compute_policy_klass
|
65
62
|
|
66
|
-
|
63
|
+
# rubocop:disable Metrics/LineLength
|
64
|
+
policy = if Object.const_defined?('Pragma::Policy::Base') && authorizable.is_a?(Pragma::Policy::Base)
|
67
65
|
authorizable
|
68
66
|
else
|
69
67
|
build_policy(authorizable)
|
70
68
|
end
|
69
|
+
# rubocop:enable Metrics/LineLength
|
71
70
|
|
72
71
|
params.each_pair do |name, value|
|
73
72
|
next unless policy.resource.respond_to?("#{name}=")
|
@@ -102,13 +101,22 @@ module Pragma
|
|
102
101
|
#
|
103
102
|
# @return [Pragma::Decorator::Base|Enumerable]
|
104
103
|
def authorize_collection(collection)
|
105
|
-
|
104
|
+
policy_klass = compute_policy_klass
|
105
|
+
return collection unless policy_klass
|
106
106
|
|
107
|
-
|
107
|
+
policy_klass.accessible_by(
|
108
108
|
user: current_user,
|
109
109
|
scope: collection
|
110
110
|
)
|
111
111
|
end
|
112
|
+
|
113
|
+
def compute_policy_klass
|
114
|
+
if self.class.policy_klass.is_a?(Proc)
|
115
|
+
self.class.policy_klass.call(context)
|
116
|
+
else
|
117
|
+
self.class.policy_klass
|
118
|
+
end
|
119
|
+
end
|
112
120
|
end
|
113
121
|
end
|
114
122
|
end
|
@@ -11,11 +11,18 @@ module Pragma
|
|
11
11
|
end
|
12
12
|
|
13
13
|
module ClassMethods # :nodoc:
|
14
|
-
# Sets the decorator to use for
|
14
|
+
# Sets the decorator to use for decorating this operation.
|
15
15
|
#
|
16
16
|
# @param klass [Class] a subclass of +Pragma::Decorator::Base+
|
17
|
-
|
18
|
-
|
17
|
+
#
|
18
|
+
# @yield A block which will be called with the operation's context which should return
|
19
|
+
# the decorator class. The block can also return +nil+ if decoration should be skipped.
|
20
|
+
def decorator(klass = nil, &block)
|
21
|
+
if !klass && !block_given?
|
22
|
+
fail ArgumentError, 'You must pass either a decorator class or a block'
|
23
|
+
end
|
24
|
+
|
25
|
+
@decorator = klass || block
|
19
26
|
end
|
20
27
|
|
21
28
|
# Returns the decorator class.
|
@@ -24,20 +31,6 @@ module Pragma
|
|
24
31
|
def decorator_klass
|
25
32
|
@decorator
|
26
33
|
end
|
27
|
-
|
28
|
-
# Builds the decorator for the given resource, using the previously defined decorator class.
|
29
|
-
#
|
30
|
-
# Works with both singular resources and collections.
|
31
|
-
#
|
32
|
-
# @param resource [Object]
|
33
|
-
#
|
34
|
-
# @return [Pragma::Decorator::Base]
|
35
|
-
#
|
36
|
-
# @see #decorator
|
37
|
-
def build_decorator(resource)
|
38
|
-
resource = resource.to_a if resource.is_a?(Enumerable)
|
39
|
-
decorator_klass.represent(resource)
|
40
|
-
end
|
41
34
|
end
|
42
35
|
|
43
36
|
module InstanceMethods # :nodoc:
|
@@ -52,7 +45,8 @@ module Pragma
|
|
52
45
|
#
|
53
46
|
# @see #decorate
|
54
47
|
def build_decorator(resource)
|
55
|
-
|
48
|
+
resource = resource.to_a if resource.is_a?(Enumerable)
|
49
|
+
compute_decorator_klass.represent(resource)
|
56
50
|
end
|
57
51
|
|
58
52
|
# If a decorator is defined, acts as an alias for {#build_decorator}. If not, simply returns
|
@@ -63,9 +57,19 @@ module Pragma
|
|
63
57
|
#
|
64
58
|
# @see #build_decorator
|
65
59
|
def decorate(decoratable)
|
66
|
-
return decoratable unless
|
60
|
+
return decoratable unless compute_decorator_klass
|
67
61
|
build_decorator(decoratable)
|
68
62
|
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def compute_decorator_klass
|
67
|
+
if self.class.decorator_klass.is_a?(Proc)
|
68
|
+
self.class.decorator_klass.call(context)
|
69
|
+
else
|
70
|
+
self.class.decorator_klass
|
71
|
+
end
|
72
|
+
end
|
69
73
|
end
|
70
74
|
end
|
71
75
|
end
|
@@ -14,30 +14,23 @@ module Pragma
|
|
14
14
|
# Sets the contract to use for validating this operation.
|
15
15
|
#
|
16
16
|
# @param klass [Class] a subclass of +Pragma::Contract::Base+
|
17
|
-
|
18
|
-
|
17
|
+
#
|
18
|
+
# @yield A block which will be called with the operation's context which should return
|
19
|
+
# the contract class. The block can also return +nil+ if validation should be skipped.
|
20
|
+
def contract(klass = nil, &block)
|
21
|
+
if !klass && !block_given?
|
22
|
+
fail ArgumentError, 'You must pass either a contract class or a block'
|
23
|
+
end
|
24
|
+
|
25
|
+
@contract = klass || block
|
19
26
|
end
|
20
27
|
|
21
28
|
# Returns the contract class.
|
22
29
|
#
|
23
|
-
# @return [Class]
|
30
|
+
# @return [Class|Proc]
|
24
31
|
def contract_klass
|
25
32
|
@contract
|
26
33
|
end
|
27
|
-
|
28
|
-
# Builds the contract for the given resource, using the previous defined contract class.
|
29
|
-
#
|
30
|
-
# If no contract has been defined for this operation, simply returns the resource.
|
31
|
-
#
|
32
|
-
# @param resource [Object]
|
33
|
-
#
|
34
|
-
# @return [Pragma::Contract::Base]
|
35
|
-
#
|
36
|
-
# @see #contract
|
37
|
-
def build_contract(resource)
|
38
|
-
return resource unless contract_klass
|
39
|
-
contract_klass.new(resource)
|
40
|
-
end
|
41
34
|
end
|
42
35
|
|
43
36
|
module InstanceMethods # :nodoc:
|
@@ -51,9 +44,11 @@ module Pragma
|
|
51
44
|
# @return [Pragma::Contract::Base]
|
52
45
|
#
|
53
46
|
# @see .contract
|
54
|
-
# @see .build_contract
|
55
47
|
def build_contract(resource)
|
56
|
-
|
48
|
+
contract_klass = compute_contract_klass(resource)
|
49
|
+
return resource unless contract_klass
|
50
|
+
|
51
|
+
contract_klass.new(resource)
|
57
52
|
end
|
58
53
|
|
59
54
|
# Validates this operation on the provided contract or resource.
|
@@ -65,7 +60,8 @@ module Pragma
|
|
65
60
|
#
|
66
61
|
# @return [Boolean] whether the operation is valid
|
67
62
|
def validate(validatable)
|
68
|
-
|
63
|
+
# rubocop:disable Metrics/LineLength
|
64
|
+
contract = if Object.const_defined?('Pragma::Contract::Base') && validatable.is_a?(Pragma::Contract::Base)
|
69
65
|
validatable
|
70
66
|
else
|
71
67
|
build_contract(validatable)
|
@@ -84,7 +80,8 @@ module Pragma
|
|
84
80
|
#
|
85
81
|
# @param validatable [Object|Pragma::Contract::Base] contract or resource
|
86
82
|
def validate!(validatable)
|
87
|
-
|
83
|
+
# rubocop:disable Metrics/LineLength
|
84
|
+
contract = if Object.const_defined?('Pragma::Contract::Base') && validatable.is_a?(Pragma::Contract::Base)
|
88
85
|
validatable
|
89
86
|
else
|
90
87
|
build_contract(validatable)
|
@@ -127,6 +124,14 @@ module Pragma
|
|
127
124
|
}
|
128
125
|
}
|
129
126
|
end
|
127
|
+
|
128
|
+
def compute_contract_klass(_resource)
|
129
|
+
if self.class.contract_klass.is_a?(Proc)
|
130
|
+
self.class.contract_klass.call(context)
|
131
|
+
else
|
132
|
+
self.class.contract_klass
|
133
|
+
end
|
134
|
+
end
|
130
135
|
end
|
131
136
|
end
|
132
137
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pragma-operation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alessandro Desantis
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-01-
|
11
|
+
date: 2017-01-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: interactor
|
@@ -198,7 +198,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
198
198
|
version: '0'
|
199
199
|
requirements: []
|
200
200
|
rubyforge_project:
|
201
|
-
rubygems_version: 2.
|
201
|
+
rubygems_version: 2.6.8
|
202
202
|
signing_key:
|
203
203
|
specification_version: 4
|
204
204
|
summary: Business logic encapsulation for your JSON API.
|