praxis 2.0.pre.2 → 2.0.pre.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +0 -1
  3. data/.ruby-version +1 -0
  4. data/CHANGELOG.md +32 -0
  5. data/Gemfile +1 -1
  6. data/Guardfile +2 -1
  7. data/Rakefile +1 -7
  8. data/TODO.md +28 -0
  9. data/lib/api_browser/package-lock.json +7110 -0
  10. data/lib/praxis.rb +7 -4
  11. data/lib/praxis/action_definition.rb +9 -16
  12. data/lib/praxis/api_general_info.rb +21 -0
  13. data/lib/praxis/application.rb +1 -2
  14. data/lib/praxis/bootloader_stages/routing.rb +2 -4
  15. data/lib/praxis/docs/generator.rb +11 -6
  16. data/lib/praxis/docs/open_api_generator.rb +255 -0
  17. data/lib/praxis/docs/openapi/info_object.rb +31 -0
  18. data/lib/praxis/docs/openapi/media_type_object.rb +59 -0
  19. data/lib/praxis/docs/openapi/operation_object.rb +40 -0
  20. data/lib/praxis/docs/openapi/parameter_object.rb +69 -0
  21. data/lib/praxis/docs/openapi/paths_object.rb +58 -0
  22. data/lib/praxis/docs/openapi/request_body_object.rb +51 -0
  23. data/lib/praxis/docs/openapi/response_object.rb +63 -0
  24. data/lib/praxis/docs/openapi/responses_object.rb +44 -0
  25. data/lib/praxis/docs/openapi/schema_object.rb +87 -0
  26. data/lib/praxis/docs/openapi/server_object.rb +24 -0
  27. data/lib/praxis/docs/openapi/tag_object.rb +21 -0
  28. data/lib/praxis/extensions/attribute_filtering.rb +2 -0
  29. data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +148 -157
  30. data/lib/praxis/extensions/attribute_filtering/active_record_patches.rb +15 -0
  31. data/lib/praxis/extensions/attribute_filtering/active_record_patches/5x.rb +90 -0
  32. data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_0.rb +68 -0
  33. data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_1_plus.rb +58 -0
  34. data/lib/praxis/extensions/attribute_filtering/filter_tree_node.rb +35 -0
  35. data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +13 -12
  36. data/lib/praxis/extensions/attribute_filtering/sequel_filter_query_builder.rb +3 -2
  37. data/lib/praxis/extensions/field_selection/active_record_query_selector.rb +24 -30
  38. data/lib/praxis/extensions/field_selection/field_selector.rb +4 -0
  39. data/lib/praxis/extensions/field_selection/sequel_query_selector.rb +32 -39
  40. data/lib/praxis/extensions/pagination.rb +130 -0
  41. data/lib/praxis/extensions/pagination/active_record_pagination_handler.rb +42 -0
  42. data/lib/praxis/extensions/pagination/header_generator.rb +70 -0
  43. data/lib/praxis/extensions/pagination/ordering_params.rb +234 -0
  44. data/lib/praxis/extensions/pagination/pagination_handler.rb +68 -0
  45. data/lib/praxis/extensions/pagination/pagination_params.rb +374 -0
  46. data/lib/praxis/extensions/pagination/sequel_pagination_handler.rb +45 -0
  47. data/lib/praxis/handlers/json.rb +2 -0
  48. data/lib/praxis/handlers/www_form.rb +5 -0
  49. data/lib/praxis/handlers/{xml.rb → xml-sample.rb} +6 -0
  50. data/lib/praxis/links.rb +4 -0
  51. data/lib/praxis/mapper/active_model_compat.rb +57 -4
  52. data/lib/praxis/mapper/resource.rb +18 -11
  53. data/lib/praxis/mapper/selector_generator.rb +99 -75
  54. data/lib/praxis/mapper/sequel_compat.rb +43 -3
  55. data/lib/praxis/media_type.rb +1 -56
  56. data/lib/praxis/multipart/part.rb +5 -2
  57. data/lib/praxis/plugins/mapper_plugin.rb +17 -3
  58. data/lib/praxis/plugins/pagination_plugin.rb +71 -0
  59. data/lib/praxis/resource_definition.rb +4 -12
  60. data/lib/praxis/response_definition.rb +1 -1
  61. data/lib/praxis/route.rb +2 -4
  62. data/lib/praxis/routing_config.rb +4 -8
  63. data/lib/praxis/tasks/api_docs.rb +23 -0
  64. data/lib/praxis/tasks/routes.rb +10 -15
  65. data/lib/praxis/types/media_type_common.rb +10 -0
  66. data/lib/praxis/types/multipart_array.rb +62 -0
  67. data/lib/praxis/validation_handler.rb +1 -2
  68. data/lib/praxis/version.rb +1 -1
  69. data/praxis.gemspec +7 -5
  70. data/spec/functional_spec.rb +9 -6
  71. data/spec/praxis/action_definition_spec.rb +4 -16
  72. data/spec/praxis/api_general_info_spec.rb +6 -6
  73. data/spec/praxis/extensions/attribute_filtering/active_record_filter_query_builder_spec.rb +304 -0
  74. data/spec/praxis/extensions/attribute_filtering/filter_tree_node_spec.rb +39 -0
  75. data/spec/praxis/extensions/attribute_filtering/filtering_params_spec.rb +34 -0
  76. data/spec/praxis/extensions/field_expansion_spec.rb +6 -24
  77. data/spec/praxis/extensions/field_selection/active_record_query_selector_spec.rb +110 -0
  78. data/spec/praxis/extensions/field_selection/sequel_query_selector_spec.rb +148 -0
  79. data/spec/praxis/extensions/pagination/active_record_pagination_handler_spec.rb +130 -0
  80. data/spec/praxis/extensions/support/spec_resources_active_model.rb +173 -0
  81. data/spec/praxis/extensions/support/spec_resources_sequel.rb +106 -0
  82. data/spec/praxis/mapper/selector_generator_spec.rb +306 -282
  83. data/spec/praxis/media_type_spec.rb +5 -129
  84. data/spec/praxis/request_spec.rb +3 -22
  85. data/spec/praxis/resource_definition_spec.rb +1 -1
  86. data/spec/praxis/response_definition_spec.rb +8 -9
  87. data/spec/praxis/route_spec.rb +2 -9
  88. data/spec/praxis/routing_config_spec.rb +4 -13
  89. data/spec/praxis/types/multipart_array_spec.rb +4 -21
  90. data/spec/spec_app/config/environment.rb +0 -2
  91. data/spec/spec_app/design/api.rb +7 -1
  92. data/spec/spec_app/design/media_types/instance.rb +0 -8
  93. data/spec/spec_app/design/media_types/volume.rb +0 -12
  94. data/spec/spec_app/design/resources/instances.rb +1 -2
  95. data/spec/spec_helper.rb +17 -0
  96. data/spec/support/be_deep_equal_matcher.rb +39 -0
  97. data/spec/support/spec_media_types.rb +0 -73
  98. data/spec/support/spec_resources.rb +42 -49
  99. metadata +75 -40
  100. data/spec/praxis/handlers/xml_spec.rb +0 -177
  101. data/spec/praxis/links_spec.rb +0 -68
  102. data/spec/spec_app/app/models/person.rb +0 -3
@@ -14,16 +14,11 @@ class Instance < Praxis::MediaType
14
14
 
15
15
  attribute :volumes, Volume::Collection
16
16
 
17
- links do
18
- link :root_volume
19
- link :other_volume, Volume, using: :data_volume
20
- end
21
17
  end
22
18
 
23
19
  view :default do
24
20
  attribute :id
25
21
  attribute :root_volume
26
- attribute :links
27
22
  end
28
23
 
29
24
  view :link do
@@ -40,9 +35,6 @@ class Instance < Praxis::MediaType
40
35
  attribute :id
41
36
  attribute :name
42
37
  attribute :root_volume
43
- attribute :links do
44
- attribute :root_volume
45
- end
46
38
  end
47
39
 
48
40
 
@@ -9,12 +9,6 @@ class Volume < Praxis::MediaType
9
9
 
10
10
  attribute :snapshots, Praxis::Collection.of(VolumeSnapshot)
11
11
  attribute :snapshots_summary, VolumeSnapshot::CollectionSummary
12
-
13
- links do
14
- link :source
15
- link :snapshots, VolumeSnapshot::CollectionSummary, using: :snapshots_summary
16
- end
17
-
18
12
  end
19
13
 
20
14
  view :default do
@@ -22,12 +16,6 @@ class Volume < Praxis::MediaType
22
16
  attribute :name
23
17
  attribute :source
24
18
  attribute :snapshots
25
-
26
- attribute :links
27
- end
28
-
29
- view :link do
30
- attribute :id
31
19
  end
32
20
 
33
21
  class Collection < Praxis::Collection
@@ -44,7 +44,6 @@ module ApiResources
44
44
  action :show do
45
45
  routing do
46
46
  get '/:id'
47
- get '/something/:id', name: :alternate
48
47
  end
49
48
 
50
49
  response :ok, media_type: 'application/json'
@@ -144,7 +143,7 @@ module ApiResources
144
143
  attribute :id
145
144
  end
146
145
 
147
- payload do
146
+ payload required: false do
148
147
  attribute :when, DateTime
149
148
  end
150
149
 
@@ -7,6 +7,12 @@ $:.unshift File.expand_path('support',__dir__)
7
7
 
8
8
  require 'bundler'
9
9
  Bundler.setup :default, :test
10
+
11
+ RSpec.configure do |config|
12
+ config.filter_run focus: true
13
+ config.run_all_when_everything_filtered = true
14
+ end
15
+
10
16
  require 'simplecov'
11
17
  SimpleCov.start 'praxis'
12
18
 
@@ -23,10 +29,21 @@ Dir["#{File.dirname(__FILE__)}/../lib/praxis/plugins/*.rb"].each do |file|
23
29
  require file
24
30
  end
25
31
 
32
+
26
33
  Dir["#{File.dirname(__FILE__)}/support/*.rb"].each do |file|
27
34
  require file
28
35
  end
29
36
 
37
+ def suppress_output
38
+ original_stdout, original_stderr = $stdout.clone, $stderr.clone
39
+ $stderr.reopen File.new('/dev/null', 'w')
40
+ $stdout.reopen File.new('/dev/null', 'w')
41
+ yield
42
+ ensure
43
+ $stdout.reopen original_stdout
44
+ $stderr.reopen original_stderr
45
+ end
46
+
30
47
  RSpec.configure do |config|
31
48
  config.include Rack::Test::Methods
32
49
 
@@ -0,0 +1,39 @@
1
+ # Copied verbatim from: https://github.com/amogil/rspec-deep-ignore-order-matcher
2
+
3
+ RSpec::Matchers.define :be_deep_equal do |expected|
4
+ match { |actual| match? actual, expected }
5
+
6
+ failure_message do |actual|
7
+ "expected that #{actual} would be deep equal with #{expected}"
8
+ end
9
+
10
+ failure_message_when_negated do |actual|
11
+ "expected that #{actual} would not be deep equal with #{expected}"
12
+ end
13
+
14
+ description do
15
+ "be deep equal with #{expected}"
16
+ end
17
+
18
+ def match?(actual, expected)
19
+ return arrays_match?(actual, expected) if expected.is_a?(Array) && actual.is_a?(Array)
20
+ return hashes_match?(actual, expected) if expected.is_a?(Hash) && actual.is_a?(Hash)
21
+ expected == actual
22
+ end
23
+
24
+ def arrays_match?(actual, expected)
25
+ exp = expected.clone
26
+ actual.each do |a|
27
+ index = exp.find_index { |e| match? a, e }
28
+ return false if index.nil?
29
+ exp.delete_at(index)
30
+ end
31
+ exp.empty?
32
+ end
33
+
34
+ def hashes_match?(actual, expected)
35
+ return false unless actual.keys.sort == expected.keys.sort
36
+ actual.each { |key, value| return false unless match? value, expected[key] }
37
+ true
38
+ end
39
+ end
@@ -6,22 +6,13 @@ class Person < Praxis::MediaType
6
6
  attribute :id, Integer
7
7
  attribute :name, String, example: /[:name:]/
8
8
  attribute :href, String, example: proc { |person| "/people/#{person.id}" }
9
- attribute :links, Attributor::Collection.of(String),
10
- description: 'Here to ensure an explicit links attribute works'
11
-
12
9
  end
13
10
 
14
11
  view :default do
15
12
  attribute :id
16
13
  attribute :name
17
- attribute :links
18
14
  end
19
15
 
20
- view :link do
21
- attribute :id
22
- attribute :name
23
- attribute :href
24
- end
25
16
 
26
17
  class CollectionSummary < Praxis::MediaType
27
18
  attributes do
@@ -29,11 +20,6 @@ class Person < Praxis::MediaType
29
20
  attribute :size, Integer
30
21
  end
31
22
 
32
- view :link do
33
- attribute :href
34
- attribute :size
35
- end
36
-
37
23
  view :default do
38
24
  attribute :href
39
25
  end
@@ -59,14 +45,6 @@ class Address < Praxis::MediaType
59
45
  attribute :residents_summary, Person::CollectionSummary
60
46
 
61
47
  attribute :fields, Praxis::Types::FieldSelector.for(Person)
62
-
63
- links do
64
- link :owner
65
- link :super, Person, using: :manager
66
- link :caretaker, using: :custodian
67
- link :residents, using: :residents_summary
68
- end
69
-
70
48
  end
71
49
 
72
50
  view :default do
@@ -74,15 +52,7 @@ class Address < Praxis::MediaType
74
52
  attribute :name
75
53
  attribute :owner
76
54
  attribute :fields
77
-
78
- attribute :links
79
- end
80
-
81
- view :link do
82
- attribute :id
83
- attribute :name
84
55
  end
85
-
86
56
  end
87
57
 
88
58
 
@@ -123,10 +93,6 @@ class Blog < Praxis::MediaType
123
93
  example: proc { |blog,ctx| Post::CollectionSummary.example(ctx, href: "#{blog.href}/posts") },
124
94
  description: "Summary of information from related Post resources"
125
95
 
126
- links do
127
- link :posts, using: :posts_summary
128
- link :owner
129
- end
130
96
  end
131
97
 
132
98
  view :default do
@@ -138,7 +104,6 @@ class Blog < Praxis::MediaType
138
104
  attribute :timestamps
139
105
 
140
106
  attribute :owner
141
- attribute :links
142
107
  end
143
108
 
144
109
  view :overview do
@@ -146,11 +111,6 @@ class Blog < Praxis::MediaType
146
111
  attribute :name
147
112
  attribute :description
148
113
  end
149
-
150
- view :link do
151
- attribute :href
152
- end
153
-
154
114
  end
155
115
 
156
116
 
@@ -185,11 +145,6 @@ class Post < Praxis::MediaType
185
145
  attribute :created_at, DateTime
186
146
  attribute :updated_at, DateTime
187
147
  end
188
-
189
- links do
190
- link :author
191
- link :blog
192
- end
193
148
  end
194
149
 
195
150
  view :default do
@@ -202,23 +157,13 @@ class Post < Praxis::MediaType
202
157
  attribute :author
203
158
 
204
159
  attribute :timestamps
205
- attribute :links
206
- end
207
-
208
- view :link do
209
- attribute :href
210
160
  end
211
161
 
212
-
213
162
  class CollectionSummary < Praxis::MediaType
214
163
  attributes do
215
164
  attribute :href, String
216
165
  attribute :count, Integer
217
166
  end
218
-
219
- view :link do
220
- attribute :href
221
- end
222
167
  end
223
168
  end
224
169
 
@@ -251,12 +196,6 @@ class User < Praxis::MediaType
251
196
 
252
197
  attribute :posts_summary, Post::CollectionSummary,
253
198
  example: proc { |user,ctx| Post::CollectionSummary.example(ctx, href: "#{user.href}/posts") }
254
-
255
- links do
256
- link :posts, using: :posts_summary
257
- link :primary_blog
258
- end
259
-
260
199
  end
261
200
 
262
201
  view :default do
@@ -265,8 +204,6 @@ class User < Praxis::MediaType
265
204
 
266
205
  attribute :first
267
206
  attribute :last
268
-
269
- attribute :links
270
207
  end
271
208
 
272
209
  view :extended do
@@ -276,20 +213,10 @@ class User < Praxis::MediaType
276
213
  attribute :first
277
214
  attribute :last
278
215
  attribute :primary_blog, view: :overview
279
-
280
- attribute :links
281
216
  end
282
217
 
283
218
  view :with_post_links do
284
219
  attribute :id
285
220
  attribute :posts, view: :link
286
221
  end
287
-
288
- view :link do
289
- attribute :href
290
- end
291
-
292
- view :summary do
293
- attribute :links
294
- end
295
222
  end
@@ -8,12 +8,16 @@ class SimpleModel < OpenStruct
8
8
  parent: {
9
9
  model: ParentModel,
10
10
  primary_key: :id,
11
- type: :many_to_one
11
+ type: :many_to_one,
12
+ local_key_columns: [:parent_id],
13
+ remote_key_columns: [:id]
12
14
  },
13
15
  other_model: {
14
16
  model: OtherModel,
15
17
  primary_key: :id,
16
- type: :many_to_one
18
+ type: :many_to_one,
19
+ local_key_columns: [:other_model_id],
20
+ remote_key_columns: [:id]
17
21
  }
18
22
  }
19
23
  end
@@ -23,14 +27,20 @@ class OtherModel < OpenStruct
23
27
  include Praxis::Mapper::ActiveModelCompat
24
28
  def self._praxis_associations
25
29
  {
26
- }
27
- end
28
- end
29
-
30
- class PersonModel < OpenStruct
31
- include Praxis::Mapper::ActiveModelCompat
32
- def self._praxis_associations
33
- {
30
+ parent: {
31
+ model: ParentModel,
32
+ primary_key: :id,
33
+ type: :many_to_one,
34
+ local_key_columns: [:parent_id],
35
+ remote_key_columns: [:id]
36
+ },
37
+ simple_models: {
38
+ model: SimpleModel,
39
+ primary_key: :id,
40
+ type: :many_to_many,
41
+ local_key_columns: [:id],
42
+ remote_key_columns: [:id] # The through table is in the middle where the FKs are...
43
+ }
34
44
  }
35
45
  end
36
46
  end
@@ -39,6 +49,13 @@ class ParentModel < OpenStruct
39
49
  include Praxis::Mapper::ActiveModelCompat
40
50
  def self._praxis_associations
41
51
  {
52
+ simple_children: {
53
+ model: SimpleModel,
54
+ primary_key: :id,
55
+ type: :one_to_many,
56
+ local_key_columns: [:id],
57
+ remote_key_columns: [:parent_id]
58
+ }
42
59
  }
43
60
  end
44
61
  end
@@ -50,7 +67,9 @@ class YamlArrayModel < OpenStruct
50
67
  parents: {
51
68
  model: ParentModel,
52
69
  primary_key: :id,
53
- type: :one_to_many
70
+ type: :one_to_many,
71
+ local_key_columns: [:id],
72
+ remote_key_columns: [:parent_id]
54
73
  }
55
74
  }
56
75
  end
@@ -66,12 +85,10 @@ class BaseResource < Praxis::Mapper::Resource
66
85
  property :href, dependencies: [:id]
67
86
  end
68
87
 
69
- # class CompositeIdResource < BaseResource
70
- # model CompositeIdModel
71
- # end
72
-
73
88
  class OtherResource < BaseResource
74
89
  model OtherModel
90
+
91
+ property :display_name, dependencies: [:name]
75
92
  end
76
93
 
77
94
  class ParentResource < BaseResource
@@ -87,45 +104,21 @@ class SimpleResource < BaseResource
87
104
  self.other_model
88
105
  end
89
106
 
107
+ property :aliased_method, dependencies: [:column1, :other_model]
90
108
  property :other_resource, dependencies: [:other_model]
91
109
 
110
+ property :parent, dependencies: [:parent, :added_column]
111
+
92
112
  property :name, dependencies: [:simple_name]
93
- end
113
+ property :direct_other_name, dependencies: [ 'other_model.name' ]
114
+ property :aliased_other_name, dependencies: [ 'other_model.display_name' ]
94
115
 
95
- # class SimplerResource < BaseResource
96
- # model SimplerModel
97
- # end
116
+ property :everything, dependencies: [:*]
117
+ property :everything_from_parent, dependencies: ['parent.*']
118
+ property :circular_dep, dependencies: [ :circular_dep, :column1 ]
119
+ property :no_deps, dependencies: []
120
+ end
98
121
 
99
122
  class YamlArrayResource < BaseResource
100
123
  model YamlArrayModel
101
124
  end
102
-
103
- class PersonResource < BaseResource
104
- model PersonModel
105
-
106
- def href
107
- "/people/#{self.id}"
108
- end
109
-
110
- end
111
-
112
- # class AddressResource < BaseResource
113
- # model AddressModel
114
-
115
-
116
- # def href
117
- # "/addresses/#{self.id}"
118
- # end
119
- # property :href, dependencies: [:id]
120
-
121
- # def owner_name
122
- # self.owner.name
123
- # end
124
- # property :owner_name, dependencies: ['owner.name']
125
-
126
- # def resident_count
127
- # self.residents.size
128
- # end
129
- # property :resident_count, dependencies: [:residents]
130
-
131
- # end