pragma 2.1.1 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
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