pragma 2.1.1 → 2.2.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.
Files changed (44) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +23 -1
  4. data/README.md +22 -15
  5. data/lib/pragma.rb +21 -15
  6. data/lib/pragma/filter/base.rb +17 -0
  7. data/lib/pragma/filter/equals.rb +18 -0
  8. data/lib/pragma/filter/ilike.rb +18 -0
  9. data/lib/pragma/filter/like.rb +18 -0
  10. data/lib/pragma/filter/scope.rb +18 -0
  11. data/lib/pragma/filter/where.rb +18 -0
  12. data/lib/pragma/macro/classes.rb +98 -0
  13. data/lib/pragma/macro/contract/build.rb +25 -0
  14. data/lib/pragma/macro/contract/persist.rb +25 -0
  15. data/lib/pragma/macro/contract/validate.rb +25 -0
  16. data/lib/pragma/macro/decorator.rb +80 -0
  17. data/lib/pragma/macro/filtering.rb +45 -0
  18. data/lib/pragma/macro/model.rb +28 -0
  19. data/lib/pragma/macro/ordering.rb +81 -0
  20. data/lib/pragma/macro/pagination.rb +94 -0
  21. data/lib/pragma/macro/policy.rb +41 -0
  22. data/lib/pragma/operation/create.rb +1 -1
  23. data/lib/pragma/operation/destroy.rb +2 -2
  24. data/lib/pragma/operation/filter.rb +7 -0
  25. data/lib/pragma/operation/index.rb +3 -3
  26. data/lib/pragma/operation/macro.rb +7 -0
  27. data/lib/pragma/operation/show.rb +1 -1
  28. data/lib/pragma/operation/update.rb +1 -1
  29. data/lib/pragma/version.rb +1 -1
  30. metadata +21 -17
  31. data/lib/pragma/operation/filter/base.rb +0 -20
  32. data/lib/pragma/operation/filter/equals.rb +0 -13
  33. data/lib/pragma/operation/filter/ilike.rb +0 -13
  34. data/lib/pragma/operation/filter/like.rb +0 -13
  35. data/lib/pragma/operation/macro/classes.rb +0 -102
  36. data/lib/pragma/operation/macro/contract/build.rb +0 -27
  37. data/lib/pragma/operation/macro/contract/persist.rb +0 -27
  38. data/lib/pragma/operation/macro/contract/validate.rb +0 -27
  39. data/lib/pragma/operation/macro/decorator.rb +0 -82
  40. data/lib/pragma/operation/macro/filtering.rb +0 -47
  41. data/lib/pragma/operation/macro/model.rb +0 -30
  42. data/lib/pragma/operation/macro/ordering.rb +0 -84
  43. data/lib/pragma/operation/macro/pagination.rb +0 -96
  44. data/lib/pragma/operation/macro/policy.rb +0 -40
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pragma
4
+ module Operation
5
+ Macro = Pragma::Macro
6
+ end
7
+ end
@@ -10,7 +10,7 @@ module Pragma
10
10
  step Macro::Model(:find_by)
11
11
  step Macro::Policy()
12
12
  step Macro::Decorator()
13
- step :respond!
13
+ step :respond!, name: 'respond'
14
14
 
15
15
  def respond!(options)
16
16
  options['result.response'] = Response::Ok.new(entity: options['result.decorator.instance'])
@@ -13,7 +13,7 @@ module Pragma
13
13
  step Macro::Contract::Validate()
14
14
  step Macro::Contract::Persist()
15
15
  step Macro::Decorator()
16
- step :respond!
16
+ step :respond!, name: 'respond'
17
17
 
18
18
  def respond!(options)
19
19
  options['result.response'] = Response::Ok.new(entity: options['result.decorator.instance'])
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pragma
4
- VERSION = '2.1.1'
4
+ VERSION = '2.2.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pragma
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ version: 2.2.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: 2018-01-15 00:00:00.000000000 Z
11
+ date: 2018-02-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pragma-contract
@@ -198,23 +198,27 @@ files:
198
198
  - bin/setup
199
199
  - lib/pragma.rb
200
200
  - lib/pragma/decorator/error.rb
201
+ - lib/pragma/filter/base.rb
202
+ - lib/pragma/filter/equals.rb
203
+ - lib/pragma/filter/ilike.rb
204
+ - lib/pragma/filter/like.rb
205
+ - lib/pragma/filter/scope.rb
206
+ - lib/pragma/filter/where.rb
207
+ - lib/pragma/macro/classes.rb
208
+ - lib/pragma/macro/contract/build.rb
209
+ - lib/pragma/macro/contract/persist.rb
210
+ - lib/pragma/macro/contract/validate.rb
211
+ - lib/pragma/macro/decorator.rb
212
+ - lib/pragma/macro/filtering.rb
213
+ - lib/pragma/macro/model.rb
214
+ - lib/pragma/macro/ordering.rb
215
+ - lib/pragma/macro/pagination.rb
216
+ - lib/pragma/macro/policy.rb
201
217
  - lib/pragma/operation/create.rb
202
218
  - lib/pragma/operation/destroy.rb
203
- - lib/pragma/operation/filter/base.rb
204
- - lib/pragma/operation/filter/equals.rb
205
- - lib/pragma/operation/filter/ilike.rb
206
- - lib/pragma/operation/filter/like.rb
219
+ - lib/pragma/operation/filter.rb
207
220
  - lib/pragma/operation/index.rb
208
- - lib/pragma/operation/macro/classes.rb
209
- - lib/pragma/operation/macro/contract/build.rb
210
- - lib/pragma/operation/macro/contract/persist.rb
211
- - lib/pragma/operation/macro/contract/validate.rb
212
- - lib/pragma/operation/macro/decorator.rb
213
- - lib/pragma/operation/macro/filtering.rb
214
- - lib/pragma/operation/macro/model.rb
215
- - lib/pragma/operation/macro/ordering.rb
216
- - lib/pragma/operation/macro/pagination.rb
217
- - lib/pragma/operation/macro/policy.rb
221
+ - lib/pragma/operation/macro.rb
218
222
  - lib/pragma/operation/show.rb
219
223
  - lib/pragma/operation/update.rb
220
224
  - lib/pragma/version.rb
@@ -239,7 +243,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
239
243
  version: '0'
240
244
  requirements: []
241
245
  rubyforge_project:
242
- rubygems_version: 2.6.13
246
+ rubygems_version: 2.7.5
243
247
  signing_key:
244
248
  specification_version: 4
245
249
  summary: A pragmatic architecture for building JSON APIs with Ruby.
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Pragma
4
- module Operation
5
- module Filter
6
- class Base
7
- attr_reader :param, :column
8
-
9
- def initialize(param:, column:)
10
- @param = param.to_sym
11
- @column = column.to_sym
12
- end
13
-
14
- def apply(*)
15
- fail NotImplementedError
16
- end
17
- end
18
- end
19
- end
20
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Pragma
4
- module Operation
5
- module Filter
6
- class Equals < Base
7
- def apply(relation:, value:)
8
- relation.where(column => value)
9
- end
10
- end
11
- end
12
- end
13
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Pragma
4
- module Operation
5
- module Filter
6
- class Ilike < Base
7
- def apply(relation:, value:)
8
- relation.where("#{column} ILIKE ?", "%#{value}%")
9
- end
10
- end
11
- end
12
- end
13
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Pragma
4
- module Operation
5
- module Filter
6
- class Like < Base
7
- def apply(relation:, value:)
8
- relation.where("#{column} LIKE ?", "%#{value}%")
9
- end
10
- end
11
- end
12
- end
13
- end
@@ -1,102 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Pragma
4
- module Operation
5
- module Macro
6
- def self.Classes
7
- step = ->(input, options) { Classes.for(input, options) }
8
- [step, name: 'classes']
9
- end
10
-
11
- module Classes
12
- class << self
13
- def for(input, options)
14
- {
15
- 'model.class' => expected_model_class(input, options),
16
- 'policy.default.class' => expected_policy_class(input, options),
17
- 'policy.default.scope.class' => expected_policy_scope_class(input, options),
18
- 'decorator.instance.class' => expected_instance_decorator_class(input, options),
19
- 'decorator.collection.class' => expected_collection_decorator_class(input, options),
20
- 'contract.default.class' => expected_contract_class(input, options)
21
- }.each_pair do |key, value|
22
- next if options[key]
23
-
24
- # FIXME: This entire block is required to trigger Rails autoloading. Ugh.
25
- begin
26
- Object.const_get(value)
27
- rescue NameError => e
28
- # We check the error message to avoid silently ignoring other NameErrors
29
- # thrown while initializing the constant.
30
- if e.message.start_with?('uninitialized constant')
31
- # Required instead of a simple equality check because loading
32
- # API::V1::Post::Contract::Index might throw "uninitialized constant
33
- # API::V1::Post::Contract" if the resource has no contracts at all.
34
- error_constant = e.message.split.last
35
- raise e unless value.sub(/\A::/, '').start_with?(error_constant)
36
- end
37
- end
38
-
39
- options[key] = if Object.const_defined?(value)
40
- Object.const_get(value)
41
- end
42
- end
43
- end
44
-
45
- private
46
-
47
- def resource_namespace(input, _options)
48
- input.class.name.split('::')[0..-3]
49
- end
50
-
51
- def root_namespace(input, options)
52
- resource_namespace = resource_namespace(input, options)
53
- return [] if resource_namespace.first == 'API'
54
- resource_namespace[0..((resource_namespace.index('API') || 1) - 1)]
55
- end
56
-
57
- def expected_model_class(input, options)
58
- [
59
- root_namespace(input, options).join('::'),
60
- resource_namespace(input, options).last
61
- ].join('::')
62
- end
63
-
64
- def expected_policy_class(input, options)
65
- [
66
- resource_namespace(input, options),
67
- 'Policy'
68
- ].join('::')
69
- end
70
-
71
- def expected_policy_scope_class(input, options)
72
- "#{expected_policy_class(input, options)}::Scope"
73
- end
74
-
75
- def expected_instance_decorator_class(input, options)
76
- [
77
- resource_namespace(input, options),
78
- 'Decorator',
79
- 'Instance'
80
- ].join('::')
81
- end
82
-
83
- def expected_collection_decorator_class(input, options)
84
- [
85
- resource_namespace(input, options),
86
- 'Decorator',
87
- 'Collection'
88
- ].join('::')
89
- end
90
-
91
- def expected_contract_class(input, options)
92
- [
93
- resource_namespace(input, options),
94
- 'Contract',
95
- input.class.name.split('::').last
96
- ].join('::')
97
- end
98
- end
99
- end
100
- end
101
- end
102
- end
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'trailblazer/operation/contract'
4
-
5
- module Pragma
6
- module Operation
7
- module Macro
8
- module Contract
9
- def self.Build(name: 'default', constant: nil, builder: nil)
10
- step = lambda do |input, options|
11
- Trailblazer::Operation::Contract::Build.for(
12
- input,
13
- options,
14
- name: name,
15
- constant: constant,
16
- builder: builder
17
- ).tap do |contract|
18
- contract.current_user = options['current_user']
19
- end
20
- end
21
-
22
- [step, name: 'contract.build']
23
- end
24
- end
25
- end
26
- end
27
- end
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'trailblazer/operation/persist'
4
-
5
- module Pragma
6
- module Operation
7
- module Macro
8
- module Contract
9
- def self.Persist(**args)
10
- step = lambda do |input, options|
11
- Trailblazer::Operation::Pipetree::Step.new(
12
- Trailblazer::Operation::Contract::Persist(**args).first
13
- ).call(input, options).tap do |result|
14
- unless result
15
- options['result.response'] = Pragma::Operation::Response::UnprocessableEntity.new(
16
- errors: options['model'].errors.messages
17
- ).decorate_with(Pragma::Decorator::Error)
18
- end
19
- end
20
- end
21
-
22
- [step, name: 'persist.save']
23
- end
24
- end
25
- end
26
- end
27
- end
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'trailblazer/operation/validate'
4
-
5
- module Pragma
6
- module Operation
7
- module Macro
8
- module Contract
9
- def self.Validate(name: 'default', **args)
10
- step = lambda do |input, options|
11
- Trailblazer::Operation::Pipetree::Step.new(
12
- Trailblazer::Operation::Contract::Validate(**args).first
13
- ).call(input, options).tap do |result|
14
- unless result
15
- options['result.response'] = Pragma::Operation::Response::UnprocessableEntity.new(
16
- errors: options['contract.default'].errors.messages
17
- ).decorate_with(Pragma::Decorator::Error)
18
- end
19
- end
20
- end
21
-
22
- [step, name: "contract.#{name}.validate"]
23
- end
24
- end
25
- end
26
- end
27
- end
@@ -1,82 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Pragma
4
- module Operation
5
- module Macro
6
- def self.Decorator(name: :instance)
7
- step = ->(input, options) { Decorator.for(input, name, options) }
8
- [step, name: "decorator.#{name}"]
9
- end
10
-
11
- module Decorator
12
- class << self
13
- def for(_input, name, options)
14
- set_defaults(options)
15
-
16
- return false unless validate_params(options)
17
-
18
- options["result.decorator.#{name}"] = options["decorator.#{name}.class"].new(
19
- options['model']
20
- )
21
-
22
- validate_expansion(options, name)
23
- end
24
-
25
- private
26
-
27
- def set_defaults(options)
28
- hash_options = options.to_hash
29
-
30
- {
31
- 'expand.enabled' => true
32
- }.each_pair do |key, value|
33
- options[key] = value unless hash_options.key?(key.to_sym)
34
- end
35
- end
36
-
37
- def validate_params(options)
38
- options['contract.expand'] = Dry::Validation.Schema do
39
- optional(:expand) do
40
- if options['expand.enabled']
41
- array? do
42
- each(:str?) &
43
- # This is the ugliest, only way I found to define a dynamic validation tree.
44
- (options['expand.limit'] ? max_size?(options['expand.limit']) : array?)
45
- end
46
- else
47
- none? | empty?
48
- end
49
- end
50
- end
51
-
52
- options['result.contract.expand'] = options['contract.expand'].call(options['params'])
53
-
54
- if options['result.contract.expand'].errors.any?
55
- options['result.response'] = Response::UnprocessableEntity.new(
56
- errors: options['result.contract.expand'].errors
57
- ).decorate_with(Pragma::Decorator::Error)
58
-
59
- return false
60
- end
61
-
62
- true
63
- end
64
-
65
- def validate_expansion(options, name)
66
- return true unless options["result.decorator.#{name}"].respond_to?(:validate_expansion)
67
- options["result.decorator.#{name}"].validate_expansion(options['params'][:expand])
68
- true
69
- rescue Pragma::Decorator::Association::ExpansionError => e
70
- options['result.response'] = Response::BadRequest.new(
71
- entity: Pragma::Operation::Error.new(
72
- error_type: :expansion_error,
73
- error_message: e.message
74
- )
75
- ).decorate_with(Pragma::Decorator::Error)
76
- false
77
- end
78
- end
79
- end
80
- end
81
- end
82
- end
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Pragma
4
- module Operation
5
- module Macro
6
- def self.Filtering
7
- step = ->(input, options) { Filtering.for(input, options) }
8
- [step, name: 'filtering']
9
- end
10
-
11
- module Filtering
12
- class << self
13
- def for(_input, options)
14
- set_defaults(options)
15
-
16
- options['model'] = apply_filtering(options)
17
-
18
- true
19
- end
20
-
21
- private
22
-
23
- def set_defaults(options)
24
- {
25
- 'filtering.filters' => []
26
- }.each_pair do |key, value|
27
- options[key] = value unless options[key]
28
- end
29
- end
30
-
31
- def apply_filtering(options)
32
- relation = options['model']
33
-
34
- options['filtering.filters'].each do |filter|
35
- value = options['params'][filter.param]
36
- next unless value.present?
37
-
38
- relation = filter.apply(relation: options['model'], value: value)
39
- end
40
-
41
- relation
42
- end
43
- end
44
- end
45
- end
46
- end
47
- end