pragma 2.0.0 → 2.1.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 +4 -4
- data/.rubocop.yml +12 -0
- data/CHANGELOG.md +25 -5
- data/README.md +364 -10
- data/lib/pragma.rb +7 -0
- data/lib/pragma/operation/create.rb +3 -3
- data/lib/pragma/operation/destroy.rb +10 -9
- data/lib/pragma/operation/filter/base.rb +20 -0
- data/lib/pragma/operation/filter/equals.rb +13 -0
- data/lib/pragma/operation/filter/ilike.rb +13 -0
- data/lib/pragma/operation/filter/like.rb +13 -0
- data/lib/pragma/operation/index.rb +5 -5
- data/lib/pragma/operation/macro/classes.rb +6 -1
- data/lib/pragma/operation/macro/decorator.rb +32 -11
- data/lib/pragma/operation/macro/filtering.rb +47 -0
- data/lib/pragma/operation/macro/model.rb +4 -1
- data/lib/pragma/operation/macro/ordering.rb +84 -0
- data/lib/pragma/operation/macro/pagination.rb +7 -3
- data/lib/pragma/operation/macro/policy.rb +3 -3
- data/lib/pragma/operation/show.rb +2 -2
- data/lib/pragma/operation/update.rb +4 -4
- data/lib/pragma/version.rb +1 -1
- data/pragma.gemspec +4 -4
- metadata +18 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 423424f1618e9c1e91217573586e6e10b70b5a06
|
|
4
|
+
data.tar.gz: 77cfe2fd44b3f5df6f22563345b9ae61d48413e4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cea1554dba5bfdbb80680ea52d044843b7ab23ff28c04ccee072e750ae96b70cb2822963da3224b607fc06f9caa801e15ee8024037a2567dcb479fc7175c870b
|
|
7
|
+
data.tar.gz: f6af85934562c1538f576aba2e9a74dbfa2f2b60340a8e2a991a3cedfefd5edafd6eb44e23e7e80425861b4aa4cfe14d6212fb92bb7c84ac9f96f435259b2b21
|
data/.rubocop.yml
CHANGED
|
@@ -88,3 +88,15 @@ Metrics/ClassLength:
|
|
|
88
88
|
|
|
89
89
|
Metrics/BlockLength:
|
|
90
90
|
Enabled: false
|
|
91
|
+
|
|
92
|
+
Style/Documentation:
|
|
93
|
+
Enabled: false
|
|
94
|
+
|
|
95
|
+
Naming/MethodName:
|
|
96
|
+
Enabled: false
|
|
97
|
+
|
|
98
|
+
Naming/AccessorMethodName:
|
|
99
|
+
Enabled: false
|
|
100
|
+
|
|
101
|
+
RSpec/MessageSpies:
|
|
102
|
+
EnforcedStyle: receive
|
data/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
6
|
+
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [2.1.0]
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Implemented `expand.limit` and `expand.enabled`
|
|
15
|
+
- Implemented the `Ordering` macro
|
|
16
|
+
- Implemented the `Filtering` macro
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Fixed automatic lookup of nested model classes
|
|
21
|
+
|
|
22
|
+
## [2.0.0]
|
|
23
|
+
|
|
24
|
+
First Pragma 2 release.
|
|
25
|
+
|
|
26
|
+
[Unreleased]: https://github.com/pragmarb/pragma/compare/v2.1.0...HEAD
|
|
27
|
+
[2.1.0]: https://github.com/pragmarb/pragma/compare/v2.0.0...v2.1.0
|
|
28
|
+
[2.0.0]: https://github.com/pragmarb/pragma/compare/v1.2.6...v2.0.0
|
data/README.md
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# Pragma
|
|
2
2
|
|
|
3
|
-
[](https://travis-ci.org/pragmarb/pragma)
|
|
4
|
+
[](https://gemnasium.com/github.com/pragmarb/pragma)
|
|
5
|
+
[](https://coveralls.io/github/pragmarb/pragma?branch=master)
|
|
6
|
+
[](https://codeclimate.com/github/pragmarb/pragma/maintainability)
|
|
7
7
|
|
|
8
|
-
Welcome to Pragma,
|
|
8
|
+
Welcome to Pragma, an expressive, opinionated ecosystem for building beautiful RESTful APIs with
|
|
9
|
+
Ruby.
|
|
9
10
|
|
|
10
11
|
You can think of this as a meta-gem that pulls in the following pieces:
|
|
11
12
|
|
|
@@ -99,8 +100,10 @@ This gem works best if you follow the recommended structure for organizing resou
|
|
|
99
100
|
│ ├── destroy.rb
|
|
100
101
|
│ ├── index.rb
|
|
101
102
|
│ └── update.rb
|
|
103
|
+
└── decorator
|
|
104
|
+
| ├── collection.rb
|
|
105
|
+
| └── instance.rb
|
|
102
106
|
└── policy.rb
|
|
103
|
-
└── decorator.rb
|
|
104
107
|
```
|
|
105
108
|
|
|
106
109
|
Your modules and classes would, of course, follow the same structure: `API::V1::Article::Policy` and
|
|
@@ -128,10 +131,10 @@ module API
|
|
|
128
131
|
module Operation
|
|
129
132
|
class Create < Pragma::Operation::Create
|
|
130
133
|
# This assumes that you have the following:
|
|
131
|
-
#
|
|
132
|
-
#
|
|
133
|
-
#
|
|
134
|
-
#
|
|
134
|
+
# 1) an Article model
|
|
135
|
+
# 2) a Policy (responding to #create?)
|
|
136
|
+
# 3) a Create contract
|
|
137
|
+
# 4) an Instance decorator
|
|
135
138
|
end
|
|
136
139
|
end
|
|
137
140
|
end
|
|
@@ -139,6 +142,357 @@ module API
|
|
|
139
142
|
end
|
|
140
143
|
```
|
|
141
144
|
|
|
145
|
+
## Macros
|
|
146
|
+
|
|
147
|
+
The FF are implemented through their own set of macros, which take care of stuff like authorizing,
|
|
148
|
+
paginating, filtering etc.
|
|
149
|
+
|
|
150
|
+
If you want, you can use these macros in your own operations.
|
|
151
|
+
|
|
152
|
+
### Classes
|
|
153
|
+
|
|
154
|
+
**Used in:** Index, Show, Create, Update, Destroy
|
|
155
|
+
|
|
156
|
+
The `Classes` macro is responsible of tying together all the Pragma components: put it into an
|
|
157
|
+
operation and it will determine the class names of the related policy, model, decorators and
|
|
158
|
+
contract. You can override any of these classes when defining the operation or at runtime if you
|
|
159
|
+
wish.
|
|
160
|
+
|
|
161
|
+
Example usage:
|
|
162
|
+
|
|
163
|
+
```ruby
|
|
164
|
+
module API
|
|
165
|
+
module V1
|
|
166
|
+
module Article
|
|
167
|
+
module Operation
|
|
168
|
+
class Create < Pragma::Operation::Base
|
|
169
|
+
# Let the macro figure out class names.
|
|
170
|
+
step Pragma::Operation::Macro::Classes()
|
|
171
|
+
step :execute!
|
|
172
|
+
|
|
173
|
+
# But override the contract.
|
|
174
|
+
self['contract.default.class'] = Contract::CustomCreate
|
|
175
|
+
|
|
176
|
+
def execute!(options)
|
|
177
|
+
# `options` contains the following:
|
|
178
|
+
#
|
|
179
|
+
# `model.class`
|
|
180
|
+
# `policy.default.class`
|
|
181
|
+
# `policy.default.scope.class`
|
|
182
|
+
# `decorator.instance.class`
|
|
183
|
+
# `decorator.collection.class`
|
|
184
|
+
# `contract.default.class`
|
|
185
|
+
#
|
|
186
|
+
# These will be `nil` if the expected classes do not exist.
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Model
|
|
196
|
+
|
|
197
|
+
**Used in:** Index, Show, Create, Update, Destroy
|
|
198
|
+
|
|
199
|
+
The `Model` macro provides support for performing different operations with models. It can either
|
|
200
|
+
build a new instance of the model, if you are creating a new record, for instance, or it can find
|
|
201
|
+
an existing record by ID.
|
|
202
|
+
|
|
203
|
+
Example of building a new record:
|
|
204
|
+
|
|
205
|
+
```ruby
|
|
206
|
+
module API
|
|
207
|
+
module V1
|
|
208
|
+
module Article
|
|
209
|
+
module Operation
|
|
210
|
+
class Create < Pragma::Operation::Base
|
|
211
|
+
# This step can be done by Classes if you want.
|
|
212
|
+
self['model.class'] = ::Article
|
|
213
|
+
|
|
214
|
+
step Pragma::Operation::Macro::Model(:build)
|
|
215
|
+
step :save!
|
|
216
|
+
|
|
217
|
+
def save!(options)
|
|
218
|
+
# Here you'd usually validate and assign parameters before saving.
|
|
219
|
+
|
|
220
|
+
# ...
|
|
221
|
+
|
|
222
|
+
options['model'].save!
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
As we mentioned, `Model` can also be used to find a record by ID:
|
|
232
|
+
|
|
233
|
+
```ruby
|
|
234
|
+
module API
|
|
235
|
+
module V1
|
|
236
|
+
module Article
|
|
237
|
+
module Operation
|
|
238
|
+
class Show < Pragma::Operation::Base
|
|
239
|
+
# This step can be done by Classes if you want.
|
|
240
|
+
self['model.class'] = ::Article
|
|
241
|
+
|
|
242
|
+
step Pragma::Operation::Macro::Model(:find_by), fail_fast: true
|
|
243
|
+
step :respond!
|
|
244
|
+
|
|
245
|
+
def respond!(options)
|
|
246
|
+
options['result.response'] = Response::Ok.new(
|
|
247
|
+
entity: options['model']
|
|
248
|
+
)
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
In the example above, if the record is not found, the macro will respond with `404 Not Found` and a
|
|
258
|
+
descriptive error message for you. If you want to override the error handling logic, you can remove
|
|
259
|
+
the `fail_fast` option and instead implement your own `failure` step.
|
|
260
|
+
|
|
261
|
+
### Policy
|
|
262
|
+
|
|
263
|
+
**Used in:** Index, Show, Create, Update, Destroy
|
|
264
|
+
|
|
265
|
+
The `Policy` macro ensures that the current user can perform an operation on a given record.
|
|
266
|
+
|
|
267
|
+
Here's a usage example:
|
|
268
|
+
|
|
269
|
+
```ruby
|
|
270
|
+
module API
|
|
271
|
+
module V1
|
|
272
|
+
module Article
|
|
273
|
+
module Operation
|
|
274
|
+
class Show < Pragma::Operation::Base
|
|
275
|
+
# This step can be done by Classes if you want.
|
|
276
|
+
self['policy.default.class'] = Policy
|
|
277
|
+
|
|
278
|
+
step :model!
|
|
279
|
+
step Pragma::Operation::Macro::Policy(), fail_fast: true
|
|
280
|
+
step :respond!
|
|
281
|
+
|
|
282
|
+
def model!(params:, **)
|
|
283
|
+
options['model'] = ::Article.find(params[:id])
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
If the user is not authorized to perform the operation (i.e. if the policy's `#show?` method returns
|
|
293
|
+
`false`), the macro will respond with `403 Forbidden` and a descriptive error message. If you want
|
|
294
|
+
to override the error handling logic, you can remove the `fail_fast` option and instead implement
|
|
295
|
+
your own `failure` step.
|
|
296
|
+
|
|
297
|
+
### Filtering
|
|
298
|
+
|
|
299
|
+
**Used in:** Index
|
|
300
|
+
|
|
301
|
+
The `Filtering` macro provides a simple interface to define basic filters for your API. You simply
|
|
302
|
+
include the macro and configure which filters you want to expose to the users.
|
|
303
|
+
|
|
304
|
+
```ruby
|
|
305
|
+
module API
|
|
306
|
+
module V1
|
|
307
|
+
module Article
|
|
308
|
+
module Operation
|
|
309
|
+
class Index < Pragma::Operation::Base
|
|
310
|
+
step :model!
|
|
311
|
+
step Pragma::Operation::Macro::Filtering()
|
|
312
|
+
step :respond!
|
|
313
|
+
|
|
314
|
+
self['filtering.filters'] = [
|
|
315
|
+
Pragma::Operation::Filter::Equals.new(param: :by_category, column: :category_id),
|
|
316
|
+
Pragma::Operation::Filter::Ilike.new(param: :by_title, column: :title)
|
|
317
|
+
]
|
|
318
|
+
|
|
319
|
+
def model!(params:, **)
|
|
320
|
+
options['model'] = ::Article.all
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
With the example above, you are exposing the `by_category` filter and the `by_title` filters. The
|
|
330
|
+
following filters are available for ActiveRecord currently:
|
|
331
|
+
|
|
332
|
+
- `Equals`: performs an equality (`=`) comparison.
|
|
333
|
+
- `Like`: performs a `LIKE` comparison.
|
|
334
|
+
- `Ilike`: performs an `ILIKE` comparison.
|
|
335
|
+
|
|
336
|
+
Support for more clauses as well as more ORMs will come soon.
|
|
337
|
+
|
|
338
|
+
### Ordering
|
|
339
|
+
|
|
340
|
+
**Used in:** Index
|
|
341
|
+
|
|
342
|
+
As the name suggests, the `Ordering` macro allows you to easily implement default and user-defined
|
|
343
|
+
ordering.
|
|
344
|
+
|
|
345
|
+
Here's an example:
|
|
346
|
+
|
|
347
|
+
```ruby
|
|
348
|
+
module API
|
|
349
|
+
module V1
|
|
350
|
+
module Article
|
|
351
|
+
module Operation
|
|
352
|
+
class Index < Pragma::Operation::Base
|
|
353
|
+
# This step can be done by Classes if you want.
|
|
354
|
+
self['model.class'] = ::Article
|
|
355
|
+
|
|
356
|
+
self['ordering.default_column'] = :published_at
|
|
357
|
+
self['ordering.default_direction'] = :desc
|
|
358
|
+
self['ordering.columns'] = %i[title published_at updated_at]
|
|
359
|
+
|
|
360
|
+
step :model!
|
|
361
|
+
|
|
362
|
+
# This will override `model` with the ordered relation.
|
|
363
|
+
step Pragma::Operation::Macro::Ordering(), fail_fast: true
|
|
364
|
+
|
|
365
|
+
step :respond!
|
|
366
|
+
|
|
367
|
+
def model!(options)
|
|
368
|
+
options['model'] = options['model.class'].all
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
def respond!(options)
|
|
372
|
+
options['result.response'] = Response::Ok.new(
|
|
373
|
+
entity: options['model']
|
|
374
|
+
)
|
|
375
|
+
end
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
If the user provides an invalid order column or direction, the macro will respond with `422 Unprocessable Entity`
|
|
384
|
+
and a descriptive error message. If you wish to implement your own error handling logic, you can
|
|
385
|
+
remove the `fail_fast` option and implement your own `failure` step.
|
|
386
|
+
|
|
387
|
+
The macro accepts the following options, which can be defined on the operation or at runtime:
|
|
388
|
+
|
|
389
|
+
- `ordering.columns`: an array of columns the user can order by.
|
|
390
|
+
- `ordering.default_column`: the default column to order by (default: `created_at`).
|
|
391
|
+
- `ordering.default_direction`: the default direction to order by (default: `desc`).
|
|
392
|
+
- `ordering.column_param`: the name of the parameter which will contain the order column.
|
|
393
|
+
- `ordering.direction_param`: the name of the parameter which will contain the order direction.
|
|
394
|
+
|
|
395
|
+
### Pagination
|
|
396
|
+
|
|
397
|
+
**Used in:** Index
|
|
398
|
+
|
|
399
|
+
The `Pagination` macro is responsible for paginating collections of records through
|
|
400
|
+
[will_paginate](https://github.com/mislav/will_paginate). It also allows your users to set the
|
|
401
|
+
number of records per page.
|
|
402
|
+
|
|
403
|
+
```ruby
|
|
404
|
+
module API
|
|
405
|
+
module V1
|
|
406
|
+
module Article
|
|
407
|
+
module Operation
|
|
408
|
+
class Index < Pragma::Operation::Base
|
|
409
|
+
# This step can be done by Classes if you want.
|
|
410
|
+
self['model.class'] = ::Article
|
|
411
|
+
|
|
412
|
+
step :model!
|
|
413
|
+
|
|
414
|
+
# This will override `model` with the paginated relation.
|
|
415
|
+
step Pragma::Operation::Macro::Pagination(), fail_fast: true
|
|
416
|
+
|
|
417
|
+
step :respond!
|
|
418
|
+
|
|
419
|
+
def model!(options)
|
|
420
|
+
options['model'] = options['model.class'].all
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
def respond!(options)
|
|
424
|
+
options['result.response'] = Response::Ok.new(
|
|
425
|
+
entity: options['model']
|
|
426
|
+
)
|
|
427
|
+
end
|
|
428
|
+
end
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
end
|
|
432
|
+
end
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
In the example above, if the page or per-page number fail validation, the macro will respond with
|
|
436
|
+
`422 Unprocessable Entity` and a descriptive error message. If you wish to implement your own error
|
|
437
|
+
handling logic, you can remove the `fail_fast` option and implement your own `failure` step.
|
|
438
|
+
|
|
439
|
+
The macro accepts the following options, which can be defined on the operation or at runtime:
|
|
440
|
+
|
|
441
|
+
- `pagination.page_param`: the parameter that will contain the page number.
|
|
442
|
+
- `pagination.per_page_param`: the parameter that will contain the number of items to include in each page.
|
|
443
|
+
- `pagination.default_per_page`: the default number of items per page.
|
|
444
|
+
- `pagination.max_per_page`: the max number of items per page.
|
|
445
|
+
|
|
446
|
+
This macro is best used in conjunction with the [Collection](https://github.com/pragmarb/pragma-decorator#collection)
|
|
447
|
+
and [Pagination](https://github.com/pragmarb/pragma-decorator#pagination) modules of
|
|
448
|
+
[Pragma::Decorator](https://github.com/pragmarb/pragma-decorator), which will expose all the
|
|
449
|
+
pagination metadata.
|
|
450
|
+
|
|
451
|
+
### Decorator
|
|
452
|
+
|
|
453
|
+
**Used in:** Index, Show, Create, Update
|
|
454
|
+
|
|
455
|
+
The `Decorator` macro uses one of your decorators to decorate the model. If you are using
|
|
456
|
+
[expansion](https://github.com/pragmarb/pragma-decorator#associations), it will also make sure that
|
|
457
|
+
the expansion parameter is valid.
|
|
458
|
+
|
|
459
|
+
Example usage:
|
|
460
|
+
|
|
461
|
+
```ruby
|
|
462
|
+
module API
|
|
463
|
+
module V1
|
|
464
|
+
module Article
|
|
465
|
+
module Operation
|
|
466
|
+
class Show < Pragma::Operation::Base
|
|
467
|
+
# This step can be done by Classes if you want.
|
|
468
|
+
self['decorator.instance.class'] = Decorator::Instance
|
|
469
|
+
|
|
470
|
+
step :model!
|
|
471
|
+
step Pragma::Operation::Macro::Decorator(), fail_fast: true
|
|
472
|
+
step :respond!
|
|
473
|
+
|
|
474
|
+
def model!(params:, **)
|
|
475
|
+
options['model'] = ::Article.find(params[:id])
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
def respond!(options)
|
|
479
|
+
# Pragma does this for you in the default operations.
|
|
480
|
+
options['result.response'] = Response::Ok.new(
|
|
481
|
+
entity: options['result.decorator.instance']
|
|
482
|
+
)
|
|
483
|
+
end
|
|
484
|
+
end
|
|
485
|
+
end
|
|
486
|
+
end
|
|
487
|
+
end
|
|
488
|
+
end
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
The macro accepts the following options, which can be defined on the operation or at runtime:
|
|
492
|
+
|
|
493
|
+
- `expand.enabled`: whether associations can be expanded.
|
|
494
|
+
- `expand.limit`: how many associations can be expanded at once.
|
|
495
|
+
|
|
142
496
|
## Contributing
|
|
143
497
|
|
|
144
498
|
Bug reports and pull requests are welcome on GitHub at https://github.com/pragmarb/pragma.
|
data/lib/pragma.rb
CHANGED
|
@@ -12,8 +12,15 @@ require 'pragma/version'
|
|
|
12
12
|
|
|
13
13
|
require 'pragma/decorator/error'
|
|
14
14
|
|
|
15
|
+
require 'pragma/operation/filter/base'
|
|
16
|
+
require 'pragma/operation/filter/equals'
|
|
17
|
+
require 'pragma/operation/filter/like'
|
|
18
|
+
require 'pragma/operation/filter/ilike'
|
|
19
|
+
|
|
15
20
|
require 'pragma/operation/macro/classes'
|
|
16
21
|
require 'pragma/operation/macro/decorator'
|
|
22
|
+
require 'pragma/operation/macro/filtering'
|
|
23
|
+
require 'pragma/operation/macro/ordering'
|
|
17
24
|
require 'pragma/operation/macro/pagination'
|
|
18
25
|
require 'pragma/operation/macro/policy'
|
|
19
26
|
require 'pragma/operation/macro/model'
|
|
@@ -8,10 +8,10 @@ module Pragma
|
|
|
8
8
|
class Create < Pragma::Operation::Base
|
|
9
9
|
step Macro::Classes()
|
|
10
10
|
step Macro::Model()
|
|
11
|
-
step Macro::Policy()
|
|
11
|
+
step Macro::Policy()
|
|
12
12
|
step Macro::Contract::Build()
|
|
13
|
-
step Macro::Contract::Validate()
|
|
14
|
-
step Macro::Contract::Persist()
|
|
13
|
+
step Macro::Contract::Validate()
|
|
14
|
+
step Macro::Contract::Persist()
|
|
15
15
|
step Macro::Decorator()
|
|
16
16
|
step :respond!
|
|
17
17
|
|
|
@@ -7,20 +7,21 @@ module Pragma
|
|
|
7
7
|
# @author Alessandro Desantis
|
|
8
8
|
class Destroy < Pragma::Operation::Base
|
|
9
9
|
step Macro::Classes()
|
|
10
|
-
step Macro::Model(:find_by)
|
|
11
|
-
step Macro::Policy()
|
|
10
|
+
step Macro::Model(:find_by)
|
|
11
|
+
step Macro::Policy()
|
|
12
12
|
step :destroy!
|
|
13
|
-
failure :handle_invalid_model!, fail_fast: true
|
|
14
13
|
step :respond!
|
|
15
14
|
|
|
16
15
|
def destroy!(_options, model:, **)
|
|
17
|
-
model.destroy
|
|
18
|
-
|
|
16
|
+
unless model.destroy
|
|
17
|
+
options['result.response'] = Response::UnprocessableEntity.new(
|
|
18
|
+
errors: model.errors
|
|
19
|
+
).decorate_with(Decorator::Error)
|
|
20
|
+
|
|
21
|
+
return false
|
|
22
|
+
end
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
options['result.response'] = Response::UnprocessableEntity.new(
|
|
22
|
-
errors: model.errors
|
|
23
|
-
).decorate_with(Decorator::Error)
|
|
24
|
+
true
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
def respond!(options)
|
|
@@ -0,0 +1,20 @@
|
|
|
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,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'trailblazer/dsl'
|
|
4
|
-
|
|
5
3
|
module Pragma
|
|
6
4
|
module Operation
|
|
7
5
|
# Finds all records of the requested resource, authorizes them, paginates them and decorates
|
|
@@ -12,8 +10,10 @@ module Pragma
|
|
|
12
10
|
step Macro::Classes()
|
|
13
11
|
step :retrieve!
|
|
14
12
|
step :scope!
|
|
15
|
-
step Macro::
|
|
16
|
-
step Macro::
|
|
13
|
+
step Macro::Filtering()
|
|
14
|
+
step Macro::Ordering()
|
|
15
|
+
step Macro::Pagination()
|
|
16
|
+
step Macro::Decorator(name: :collection)
|
|
17
17
|
step :respond!
|
|
18
18
|
|
|
19
19
|
def retrieve!(options)
|
|
@@ -24,7 +24,7 @@ module Pragma
|
|
|
24
24
|
options['model'] = options['policy.default.scope.class'].new(current_user, model).resolve
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
def respond!(options,
|
|
27
|
+
def respond!(options, **)
|
|
28
28
|
options['result.response'] = Response::Ok.new(
|
|
29
29
|
entity: options['result.decorator.collection']
|
|
30
30
|
)
|
|
@@ -48,9 +48,14 @@ module Pragma
|
|
|
48
48
|
input.class.name.split('::')[0..-3]
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
+
def root_namespace(input, options)
|
|
52
|
+
resource_namespace = resource_namespace(input, options)
|
|
53
|
+
resource_namespace[0..((resource_namespace.index('API') || 1) - 1)]
|
|
54
|
+
end
|
|
55
|
+
|
|
51
56
|
def expected_model_class(input, options)
|
|
52
57
|
[
|
|
53
|
-
|
|
58
|
+
root_namespace(input, options).join('::'),
|
|
54
59
|
resource_namespace(input, options).last
|
|
55
60
|
].join('::')
|
|
56
61
|
end
|
|
@@ -11,10 +11,9 @@ module Pragma
|
|
|
11
11
|
module Decorator
|
|
12
12
|
class << self
|
|
13
13
|
def for(_input, name, options)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
end
|
|
14
|
+
set_defaults(options)
|
|
15
|
+
|
|
16
|
+
return false unless validate_params(options)
|
|
18
17
|
|
|
19
18
|
options["result.decorator.#{name}"] = options["decorator.#{name}.class"].new(
|
|
20
19
|
options['model']
|
|
@@ -25,20 +24,42 @@ module Pragma
|
|
|
25
24
|
|
|
26
25
|
private
|
|
27
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
|
+
|
|
28
37
|
def validate_params(options)
|
|
29
38
|
options['contract.expand'] = Dry::Validation.Schema do
|
|
30
|
-
optional(:expand)
|
|
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
|
|
31
50
|
end
|
|
32
51
|
|
|
33
52
|
options['result.contract.expand'] = options['contract.expand'].call(options['params'])
|
|
34
53
|
|
|
35
|
-
options['result.contract.expand'].errors.
|
|
36
|
-
|
|
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)
|
|
37
58
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
59
|
+
return false
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
true
|
|
42
63
|
end
|
|
43
64
|
|
|
44
65
|
def validate_expansion(options, name)
|
|
@@ -0,0 +1,47 @@
|
|
|
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
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pragma
|
|
4
|
+
module Operation
|
|
5
|
+
module Macro
|
|
6
|
+
def self.Ordering
|
|
7
|
+
step = ->(input, options) { Ordering.for(input, options) }
|
|
8
|
+
[step, name: 'ordering']
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module Ordering
|
|
12
|
+
class << self
|
|
13
|
+
def for(_input, options)
|
|
14
|
+
set_defaults(options)
|
|
15
|
+
|
|
16
|
+
unless validate_params(options)
|
|
17
|
+
handle_invalid_contract(options)
|
|
18
|
+
return false
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
order_column = order_column(options)
|
|
22
|
+
|
|
23
|
+
if order_column
|
|
24
|
+
options['model'] = options['model'].order(order_column => order_direction(options))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
true
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def set_defaults(options)
|
|
33
|
+
default_column = if options['model.class']&.method_defined?(:created_at)
|
|
34
|
+
:created_at
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
{
|
|
38
|
+
'ordering.columns' => [default_column].compact,
|
|
39
|
+
'ordering.default_column' => default_column,
|
|
40
|
+
'ordering.default_direction' => :desc,
|
|
41
|
+
'ordering.column_param' => :order_property,
|
|
42
|
+
'ordering.direction_param' => :order_direction
|
|
43
|
+
}.each_pair do |key, value|
|
|
44
|
+
options[key] = value unless options[key]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def validate_params(options)
|
|
49
|
+
options['contract.ordering'] = Dry::Validation.Schema do
|
|
50
|
+
optional(options['ordering.column_param']).filled do
|
|
51
|
+
str? & included_in?(options['ordering.columns'].map(&:to_s))
|
|
52
|
+
end
|
|
53
|
+
optional(options['ordering.direction_param']).filled do
|
|
54
|
+
str? & included_in?(%w[asc desc ASC DESC])
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
options['result.contract.ordering'] = options['contract.ordering'].call(
|
|
59
|
+
options['params']
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
options['result.contract.ordering'].errors.empty?
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def handle_invalid_contract(options)
|
|
66
|
+
options['result.response'] = Response::UnprocessableEntity.new(
|
|
67
|
+
errors: options['result.contract.ordering'].errors
|
|
68
|
+
).decorate_with(Pragma::Decorator::Error)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def order_column(options)
|
|
72
|
+
params = options['params']
|
|
73
|
+
params[options['ordering.column_param']] || options['ordering.default_column']
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def order_direction(options)
|
|
77
|
+
params = options['params']
|
|
78
|
+
params[options['ordering.direction_param']] || options['ordering.default_direction']
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -34,7 +34,7 @@ module Pragma
|
|
|
34
34
|
'pagination.default_per_page' => 30,
|
|
35
35
|
'pagination.max_per_page' => 100
|
|
36
36
|
}.each_pair do |key, value|
|
|
37
|
-
options[key]
|
|
37
|
+
options[key] = value unless options[key]
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
|
|
@@ -53,10 +53,14 @@ module Pragma
|
|
|
53
53
|
def validate_params(options)
|
|
54
54
|
options['contract.pagination'] = Dry::Validation.Schema do
|
|
55
55
|
optional(options['pagination.page_param']).filled { int? & gteq?(1) }
|
|
56
|
-
optional(options['pagination.per_page_param']).filled
|
|
56
|
+
optional(options['pagination.per_page_param']).filled do
|
|
57
|
+
int? & (gteq?(1) & lteq?(options['pagination.max_per_page']))
|
|
58
|
+
end
|
|
57
59
|
end
|
|
58
60
|
|
|
59
|
-
options['result.contract.pagination'] = options['contract.pagination'].call(
|
|
61
|
+
options['result.contract.pagination'] = options['contract.pagination'].call(
|
|
62
|
+
options['params']
|
|
63
|
+
)
|
|
60
64
|
|
|
61
65
|
options['result.contract.pagination'].errors.empty?
|
|
62
66
|
end
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'trailblazer/operation/pundit'
|
|
4
|
-
|
|
5
3
|
module Pragma
|
|
6
4
|
module Operation
|
|
7
5
|
module Macro
|
|
@@ -31,7 +29,9 @@ module Pragma
|
|
|
31
29
|
private
|
|
32
30
|
|
|
33
31
|
def handle_unauthorized!(options)
|
|
34
|
-
options['result.response'] = Pragma::Operation::Response::Forbidden.new.decorate_with(
|
|
32
|
+
options['result.response'] = Pragma::Operation::Response::Forbidden.new.decorate_with(
|
|
33
|
+
Pragma::Decorator::Error
|
|
34
|
+
)
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
end
|
|
@@ -7,8 +7,8 @@ module Pragma
|
|
|
7
7
|
# @author Alessandro Desantis
|
|
8
8
|
class Show < Pragma::Operation::Base
|
|
9
9
|
step Macro::Classes()
|
|
10
|
-
step Macro::Model(:find_by)
|
|
11
|
-
step Macro::Policy()
|
|
10
|
+
step Macro::Model(:find_by)
|
|
11
|
+
step Macro::Policy()
|
|
12
12
|
step Macro::Decorator()
|
|
13
13
|
step :respond!
|
|
14
14
|
|
|
@@ -7,11 +7,11 @@ module Pragma
|
|
|
7
7
|
# @author Alessandro Desantis
|
|
8
8
|
class Update < Pragma::Operation::Base
|
|
9
9
|
step Macro::Classes()
|
|
10
|
-
step Macro::Model(:find_by)
|
|
11
|
-
step Macro::Policy()
|
|
10
|
+
step Macro::Model(:find_by)
|
|
11
|
+
step Macro::Policy()
|
|
12
12
|
step Macro::Contract::Build()
|
|
13
|
-
step Macro::Contract::Validate()
|
|
14
|
-
step Macro::Contract::Persist()
|
|
13
|
+
step Macro::Contract::Validate()
|
|
14
|
+
step Macro::Contract::Persist()
|
|
15
15
|
step Macro::Decorator()
|
|
16
16
|
step :respond!
|
|
17
17
|
|
data/lib/pragma/version.rb
CHANGED
data/pragma.gemspec
CHANGED
|
@@ -21,17 +21,17 @@ Gem::Specification.new do |spec|
|
|
|
21
21
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
22
22
|
spec.require_paths = ['lib']
|
|
23
23
|
|
|
24
|
-
spec.add_dependency 'trailblazer', '~> 2.0'
|
|
25
|
-
spec.add_dependency 'pragma-operation', '~> 2.0'
|
|
26
|
-
spec.add_dependency 'pragma-policy', '~> 2.0'
|
|
27
24
|
spec.add_dependency 'pragma-contract', '~> 2.0'
|
|
28
25
|
spec.add_dependency 'pragma-decorator', '~> 2.0'
|
|
26
|
+
spec.add_dependency 'pragma-operation', '~> 2.0'
|
|
27
|
+
spec.add_dependency 'pragma-policy', '~> 2.0'
|
|
28
|
+
spec.add_dependency 'trailblazer', '~> 2.0'
|
|
29
29
|
spec.add_dependency 'will_paginate', '~> 3.1'
|
|
30
30
|
|
|
31
31
|
spec.add_development_dependency 'bundler'
|
|
32
|
+
spec.add_development_dependency 'coveralls'
|
|
32
33
|
spec.add_development_dependency 'rake'
|
|
33
34
|
spec.add_development_dependency 'rspec'
|
|
34
35
|
spec.add_development_dependency 'rubocop'
|
|
35
36
|
spec.add_development_dependency 'rubocop-rspec'
|
|
36
|
-
spec.add_development_dependency 'coveralls'
|
|
37
37
|
end
|
metadata
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pragma
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.1.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:
|
|
11
|
+
date: 2018-01-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
14
|
+
name: pragma-contract
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
@@ -25,7 +25,7 @@ dependencies:
|
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '2.0'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name: pragma-
|
|
28
|
+
name: pragma-decorator
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
31
|
- - "~>"
|
|
@@ -39,7 +39,7 @@ dependencies:
|
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '2.0'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: pragma-
|
|
42
|
+
name: pragma-operation
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
45
|
- - "~>"
|
|
@@ -53,7 +53,7 @@ dependencies:
|
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: '2.0'
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
|
-
name: pragma-
|
|
56
|
+
name: pragma-policy
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
59
|
- - "~>"
|
|
@@ -67,7 +67,7 @@ dependencies:
|
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
68
|
version: '2.0'
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
|
-
name:
|
|
70
|
+
name: trailblazer
|
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
|
72
72
|
requirements:
|
|
73
73
|
- - "~>"
|
|
@@ -109,7 +109,7 @@ dependencies:
|
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
110
|
version: '0'
|
|
111
111
|
- !ruby/object:Gem::Dependency
|
|
112
|
-
name:
|
|
112
|
+
name: coveralls
|
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
|
114
114
|
requirements:
|
|
115
115
|
- - ">="
|
|
@@ -123,7 +123,7 @@ dependencies:
|
|
|
123
123
|
- !ruby/object:Gem::Version
|
|
124
124
|
version: '0'
|
|
125
125
|
- !ruby/object:Gem::Dependency
|
|
126
|
-
name:
|
|
126
|
+
name: rake
|
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
|
128
128
|
requirements:
|
|
129
129
|
- - ">="
|
|
@@ -137,7 +137,7 @@ dependencies:
|
|
|
137
137
|
- !ruby/object:Gem::Version
|
|
138
138
|
version: '0'
|
|
139
139
|
- !ruby/object:Gem::Dependency
|
|
140
|
-
name:
|
|
140
|
+
name: rspec
|
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
|
142
142
|
requirements:
|
|
143
143
|
- - ">="
|
|
@@ -151,7 +151,7 @@ dependencies:
|
|
|
151
151
|
- !ruby/object:Gem::Version
|
|
152
152
|
version: '0'
|
|
153
153
|
- !ruby/object:Gem::Dependency
|
|
154
|
-
name: rubocop
|
|
154
|
+
name: rubocop
|
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
|
156
156
|
requirements:
|
|
157
157
|
- - ">="
|
|
@@ -165,7 +165,7 @@ dependencies:
|
|
|
165
165
|
- !ruby/object:Gem::Version
|
|
166
166
|
version: '0'
|
|
167
167
|
- !ruby/object:Gem::Dependency
|
|
168
|
-
name:
|
|
168
|
+
name: rubocop-rspec
|
|
169
169
|
requirement: !ruby/object:Gem::Requirement
|
|
170
170
|
requirements:
|
|
171
171
|
- - ">="
|
|
@@ -200,13 +200,19 @@ files:
|
|
|
200
200
|
- lib/pragma/decorator/error.rb
|
|
201
201
|
- lib/pragma/operation/create.rb
|
|
202
202
|
- 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
|
|
203
207
|
- lib/pragma/operation/index.rb
|
|
204
208
|
- lib/pragma/operation/macro/classes.rb
|
|
205
209
|
- lib/pragma/operation/macro/contract/build.rb
|
|
206
210
|
- lib/pragma/operation/macro/contract/persist.rb
|
|
207
211
|
- lib/pragma/operation/macro/contract/validate.rb
|
|
208
212
|
- lib/pragma/operation/macro/decorator.rb
|
|
213
|
+
- lib/pragma/operation/macro/filtering.rb
|
|
209
214
|
- lib/pragma/operation/macro/model.rb
|
|
215
|
+
- lib/pragma/operation/macro/ordering.rb
|
|
210
216
|
- lib/pragma/operation/macro/pagination.rb
|
|
211
217
|
- lib/pragma/operation/macro/policy.rb
|
|
212
218
|
- lib/pragma/operation/show.rb
|