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