pragma-operation 1.4.0 → 1.5.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: ac19a311eb12471d8c69452399e599886d163d38
4
- data.tar.gz: a9e946273b5d8367392099b62e07c7c2def07af6
3
+ metadata.gz: 0d6cc18cf80b23f1275cdea3bdbdb5f898ab18bc
4
+ data.tar.gz: 432bbd348981310bff295afb60c4e188d6767f4d
5
5
  SHA512:
6
- metadata.gz: 0bffc30f709028c91ef22eefd54b652cc392a0d1474b502f708ce0594002aa110031e20151274df27ba50fe811c770a863384f7bb425eac31a89e62075f57bd3
7
- data.tar.gz: 9e7fd12a051c7bebe37b11843f43b978533ed9489ee5f44d1ded52ec942efe473d880bae66b3ced4d7ad2d6d61037373b8a37068ae76a71da50a51d43a71b553
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
- AlignWith: variable
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
@@ -2,3 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in pragma-operation.gemspec
4
4
  gemspec
5
+
6
+ gem 'pry'
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 policy class with `#decorator`. This will give you access to `#decorate`:
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
- policy API::V1::Post::Policy
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
- def policy(klass)
18
- @policy = klass
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
- self.class.build_policy(user: current_user, resource: resource)
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 self.class.policy_klass
61
+ return true unless compute_policy_klass
65
62
 
66
- policy = if self.class.policy_klass && authorizable.is_a?(self.class.policy_klass)
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
- return collection unless self.class.policy_klass
104
+ policy_klass = compute_policy_klass
105
+ return collection unless policy_klass
106
106
 
107
- self.class.policy_klass.accessible_by(
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 validating this operation.
14
+ # Sets the decorator to use for decorating this operation.
15
15
  #
16
16
  # @param klass [Class] a subclass of +Pragma::Decorator::Base+
17
- def decorator(klass)
18
- @decorator = klass
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
- self.class.build_decorator(resource)
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 self.class.decorator_klass
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
- def contract(klass)
18
- @contract = klass
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
- self.class.build_contract(resource)
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
- contract = if self.class.contract_klass && validatable.is_a?(self.class.contract_klass)
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
- contract = if self.class.contract_klass && validatable.is_a?(self.class.contract_klass)
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
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module Pragma
3
3
  module Operation
4
- VERSION = '1.4.0'
4
+ VERSION = '1.5.0'
5
5
  end
6
6
  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.0
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-03 00:00:00.000000000 Z
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.5.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.