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.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/CHANGELOG.md +23 -1
- data/README.md +22 -15
- data/lib/pragma.rb +21 -15
- data/lib/pragma/filter/base.rb +17 -0
- data/lib/pragma/filter/equals.rb +18 -0
- data/lib/pragma/filter/ilike.rb +18 -0
- data/lib/pragma/filter/like.rb +18 -0
- data/lib/pragma/filter/scope.rb +18 -0
- data/lib/pragma/filter/where.rb +18 -0
- data/lib/pragma/macro/classes.rb +98 -0
- data/lib/pragma/macro/contract/build.rb +25 -0
- data/lib/pragma/macro/contract/persist.rb +25 -0
- data/lib/pragma/macro/contract/validate.rb +25 -0
- data/lib/pragma/macro/decorator.rb +80 -0
- data/lib/pragma/macro/filtering.rb +45 -0
- data/lib/pragma/macro/model.rb +28 -0
- data/lib/pragma/macro/ordering.rb +81 -0
- data/lib/pragma/macro/pagination.rb +94 -0
- data/lib/pragma/macro/policy.rb +41 -0
- data/lib/pragma/operation/create.rb +1 -1
- data/lib/pragma/operation/destroy.rb +2 -2
- data/lib/pragma/operation/filter.rb +7 -0
- data/lib/pragma/operation/index.rb +3 -3
- data/lib/pragma/operation/macro.rb +7 -0
- data/lib/pragma/operation/show.rb +1 -1
- data/lib/pragma/operation/update.rb +1 -1
- data/lib/pragma/version.rb +1 -1
- metadata +21 -17
- data/lib/pragma/operation/filter/base.rb +0 -20
- data/lib/pragma/operation/filter/equals.rb +0 -13
- data/lib/pragma/operation/filter/ilike.rb +0 -13
- data/lib/pragma/operation/filter/like.rb +0 -13
- data/lib/pragma/operation/macro/classes.rb +0 -102
- data/lib/pragma/operation/macro/contract/build.rb +0 -27
- data/lib/pragma/operation/macro/contract/persist.rb +0 -27
- data/lib/pragma/operation/macro/contract/validate.rb +0 -27
- data/lib/pragma/operation/macro/decorator.rb +0 -82
- data/lib/pragma/operation/macro/filtering.rb +0 -47
- data/lib/pragma/operation/macro/model.rb +0 -30
- data/lib/pragma/operation/macro/ordering.rb +0 -84
- data/lib/pragma/operation/macro/pagination.rb +0 -96
- data/lib/pragma/operation/macro/policy.rb +0 -40
@@ -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'])
|
data/lib/pragma/version.rb
CHANGED
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.
|
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-
|
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
|
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
|
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.
|
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,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
|