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 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.