praxis 2.0.pre.17 → 2.0.pre.21

Sign up to get free protection for your applications and to get access to all the features.
Files changed (235) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +54 -0
  3. data/.simplecov +3 -1
  4. data/.travis.yml +2 -1
  5. data/CHANGELOG.md +19 -0
  6. data/CONTRIBUTING.md +2 -79
  7. data/Gemfile +5 -1
  8. data/Guardfile +6 -4
  9. data/LICENSE +0 -2
  10. data/MAINTAINERS.md +1 -0
  11. data/README.md +15 -22
  12. data/Rakefile +4 -2
  13. data/bin/praxis +55 -58
  14. data/lib/praxis/action_definition/headers_dsl_compiler.rb +5 -6
  15. data/lib/praxis/action_definition.rb +65 -95
  16. data/lib/praxis/api_definition.rb +21 -29
  17. data/lib/praxis/api_general_info.rb +55 -66
  18. data/lib/praxis/application.rb +15 -32
  19. data/lib/praxis/blueprint.rb +80 -73
  20. data/lib/praxis/bootloader.rb +24 -33
  21. data/lib/praxis/bootloader_stages/environment.rb +5 -10
  22. data/lib/praxis/bootloader_stages/file_loader.rb +3 -6
  23. data/lib/praxis/bootloader_stages/plugin_config_load.rb +4 -6
  24. data/lib/praxis/bootloader_stages/plugin_config_prepare.rb +2 -2
  25. data/lib/praxis/bootloader_stages/plugin_loader.rb +3 -7
  26. data/lib/praxis/bootloader_stages/plugin_setup.rb +3 -3
  27. data/lib/praxis/bootloader_stages/routing.rb +5 -8
  28. data/lib/praxis/bootloader_stages/subgroup_loader.rb +2 -10
  29. data/lib/praxis/bootloader_stages/warn_unloaded_files.rb +15 -19
  30. data/lib/praxis/callbacks.rb +12 -11
  31. data/lib/praxis/collection.rb +11 -14
  32. data/lib/praxis/config.rb +17 -28
  33. data/lib/praxis/config_hash.rb +2 -1
  34. data/lib/praxis/controller.rb +7 -6
  35. data/lib/praxis/dispatcher.rb +34 -42
  36. data/lib/praxis/docs/open_api/info_object.rb +11 -8
  37. data/lib/praxis/docs/open_api/media_type_object.rb +18 -17
  38. data/lib/praxis/docs/open_api/operation_object.rb +7 -4
  39. data/lib/praxis/docs/open_api/parameter_object.rb +17 -14
  40. data/lib/praxis/docs/open_api/paths_object.rb +11 -9
  41. data/lib/praxis/docs/open_api/request_body_object.rb +14 -13
  42. data/lib/praxis/docs/open_api/response_object.rb +24 -18
  43. data/lib/praxis/docs/open_api/responses_object.rb +3 -1
  44. data/lib/praxis/docs/open_api/schema_object.rb +61 -29
  45. data/lib/praxis/docs/open_api/server_object.rb +5 -2
  46. data/lib/praxis/docs/open_api/tag_object.rb +9 -6
  47. data/lib/praxis/docs/open_api_generator.rb +114 -150
  48. data/lib/praxis/endpoint_definition.rb +60 -77
  49. data/lib/praxis/error_handler.rb +2 -2
  50. data/lib/praxis/exception.rb +2 -0
  51. data/lib/praxis/exceptions/config.rb +3 -1
  52. data/lib/praxis/exceptions/config_load.rb +2 -0
  53. data/lib/praxis/exceptions/config_validation.rb +3 -1
  54. data/lib/praxis/exceptions/invalid_configuration.rb +3 -1
  55. data/lib/praxis/exceptions/invalid_response.rb +3 -1
  56. data/lib/praxis/exceptions/invalid_trait.rb +3 -1
  57. data/lib/praxis/exceptions/stage_not_found.rb +3 -1
  58. data/lib/praxis/exceptions/validation.rb +4 -3
  59. data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +163 -149
  60. data/lib/praxis/extensions/attribute_filtering/active_record_patches/5x.rb +18 -13
  61. data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_0.rb +13 -9
  62. data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_1_plus.rb +14 -11
  63. data/lib/praxis/extensions/attribute_filtering/active_record_patches.rb +12 -9
  64. data/lib/praxis/extensions/attribute_filtering/filter_tree_node.rb +8 -5
  65. data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +89 -65
  66. data/lib/praxis/extensions/attribute_filtering/filters_parser.rb +68 -62
  67. data/lib/praxis/extensions/attribute_filtering.rb +3 -1
  68. data/lib/praxis/extensions/field_expansion.rb +6 -4
  69. data/lib/praxis/extensions/field_selection/active_record_query_selector.rb +10 -8
  70. data/lib/praxis/extensions/field_selection/field_selector.rb +91 -92
  71. data/lib/praxis/extensions/field_selection/sequel_query_selector.rb +12 -12
  72. data/lib/praxis/extensions/field_selection.rb +3 -1
  73. data/lib/praxis/extensions/pagination/active_record_pagination_handler.rb +6 -4
  74. data/lib/praxis/extensions/pagination/header_generator.rb +16 -11
  75. data/lib/praxis/extensions/pagination/ordering_params.rb +29 -28
  76. data/lib/praxis/extensions/pagination/pagination_handler.rb +44 -42
  77. data/lib/praxis/extensions/pagination/pagination_params.rb +29 -48
  78. data/lib/praxis/extensions/pagination/sequel_pagination_handler.rb +8 -7
  79. data/lib/praxis/extensions/pagination.rb +10 -15
  80. data/lib/praxis/extensions/rails_compat/request_methods.rb +3 -4
  81. data/lib/praxis/extensions/rails_compat.rb +2 -0
  82. data/lib/praxis/extensions/rendering.rb +12 -12
  83. data/lib/praxis/field_expander.rb +8 -9
  84. data/lib/praxis/file_group.rb +8 -12
  85. data/lib/praxis/finalizable.rb +1 -0
  86. data/lib/praxis/handlers/json.rb +5 -2
  87. data/lib/praxis/handlers/plain.rb +2 -1
  88. data/lib/praxis/handlers/www_form.rb +6 -3
  89. data/lib/praxis/handlers/{xml-sample.rb → xml_sample.rb} +26 -22
  90. data/lib/praxis/mapper/active_model_compat.rb +13 -10
  91. data/lib/praxis/mapper/resource.rb +196 -181
  92. data/lib/praxis/mapper/selector_generator.rb +106 -112
  93. data/lib/praxis/mapper/sequel_compat.rb +70 -67
  94. data/lib/praxis/media_type.rb +2 -2
  95. data/lib/praxis/media_type_identifier.rb +26 -22
  96. data/lib/praxis/middleware_app.rb +18 -15
  97. data/lib/praxis/multipart/parser.rb +46 -51
  98. data/lib/praxis/multipart/part.rb +78 -110
  99. data/lib/praxis/notifications.rb +2 -4
  100. data/lib/praxis/plugin.rb +11 -18
  101. data/lib/praxis/plugin_concern.rb +12 -15
  102. data/lib/praxis/plugins/mapper_plugin.rb +15 -13
  103. data/lib/praxis/plugins/pagination_plugin.rb +8 -6
  104. data/lib/praxis/plugins/rails_plugin.rb +33 -28
  105. data/lib/praxis/renderer.rb +11 -15
  106. data/lib/praxis/request.rb +48 -44
  107. data/lib/praxis/request_stages/action.rb +4 -6
  108. data/lib/praxis/request_stages/load_request.rb +2 -4
  109. data/lib/praxis/request_stages/request_stage.rb +19 -23
  110. data/lib/praxis/request_stages/response.rb +4 -6
  111. data/lib/praxis/request_stages/validate.rb +3 -5
  112. data/lib/praxis/request_stages/validate_params_and_headers.rb +15 -22
  113. data/lib/praxis/request_stages/validate_payload.rb +25 -28
  114. data/lib/praxis/request_superclassing.rb +3 -3
  115. data/lib/praxis/resource_definition.rb +1 -0
  116. data/lib/praxis/response.rb +24 -26
  117. data/lib/praxis/response_definition.rb +77 -122
  118. data/lib/praxis/response_template.rb +11 -15
  119. data/lib/praxis/responses/http.rb +23 -44
  120. data/lib/praxis/responses/internal_server_error.rb +18 -21
  121. data/lib/praxis/responses/multipart_ok.rb +4 -9
  122. data/lib/praxis/responses/validation_error.rb +8 -15
  123. data/lib/praxis/route.rb +8 -10
  124. data/lib/praxis/router/rack.rb +13 -7
  125. data/lib/praxis/router/simple.rb +10 -5
  126. data/lib/praxis/router.rb +27 -34
  127. data/lib/praxis/routing_config.rb +52 -29
  128. data/lib/praxis/simple_media_type.rb +5 -8
  129. data/lib/praxis/stage.rb +17 -25
  130. data/lib/praxis/tasks/api_docs.rb +17 -16
  131. data/lib/praxis/tasks/console.rb +3 -1
  132. data/lib/praxis/tasks/environment.rb +2 -0
  133. data/lib/praxis/tasks/routes.rb +26 -24
  134. data/lib/praxis/tasks.rb +3 -1
  135. data/lib/praxis/trait.rb +37 -46
  136. data/lib/praxis/types/fuzzy_hash.rb +13 -14
  137. data/lib/praxis/types/media_type_common.rb +11 -10
  138. data/lib/praxis/types/multipart_array/part_definition.rb +14 -17
  139. data/lib/praxis/types/multipart_array.rb +100 -115
  140. data/lib/praxis/validation_handler.rb +5 -3
  141. data/lib/praxis/version.rb +3 -1
  142. data/lib/praxis.rb +4 -5
  143. data/praxis.gemspec +22 -21
  144. data/spec/functional_spec.rb +44 -56
  145. data/spec/praxis/action_definition_spec.rb +39 -48
  146. data/spec/praxis/api_definition_spec.rb +45 -47
  147. data/spec/praxis/api_general_info_spec.rb +28 -29
  148. data/spec/praxis/application_spec.rb +18 -14
  149. data/spec/praxis/blueprint_spec.rb +33 -34
  150. data/spec/praxis/bootloader_spec.rb +32 -30
  151. data/spec/praxis/callbacks_spec.rb +37 -37
  152. data/spec/praxis/collection_spec.rb +18 -25
  153. data/spec/praxis/config_hash_spec.rb +5 -4
  154. data/spec/praxis/config_spec.rb +27 -26
  155. data/spec/praxis/controller_spec.rb +8 -9
  156. data/spec/praxis/endpoint_definition_spec.rb +25 -32
  157. data/spec/praxis/extensions/attribute_filtering/active_record_filter_query_builder_spec.rb +171 -114
  158. data/spec/praxis/extensions/attribute_filtering/filter_tree_node_spec.rb +22 -21
  159. data/spec/praxis/extensions/attribute_filtering/filtering_params_spec.rb +112 -60
  160. data/spec/praxis/extensions/attribute_filtering/filters_parser_spec.rb +37 -38
  161. data/spec/praxis/extensions/field_expansion_spec.rb +8 -10
  162. data/spec/praxis/extensions/field_selection/active_record_query_selector_spec.rb +14 -13
  163. data/spec/praxis/extensions/field_selection/field_selector_spec.rb +9 -16
  164. data/spec/praxis/extensions/field_selection/sequel_query_selector_spec.rb +50 -49
  165. data/spec/praxis/extensions/pagination/active_record_pagination_handler_spec.rb +32 -31
  166. data/spec/praxis/extensions/rendering_spec.rb +9 -9
  167. data/spec/praxis/extensions/support/spec_resources_active_model.rb +32 -49
  168. data/spec/praxis/extensions/support/spec_resources_sequel.rb +48 -48
  169. data/spec/praxis/field_expander_spec.rb +6 -5
  170. data/spec/praxis/file_group_spec.rb +3 -1
  171. data/spec/praxis/handlers/json_spec.rb +6 -5
  172. data/spec/praxis/mapper/resource_spec.rb +39 -29
  173. data/spec/praxis/mapper/selector_generator_spec.rb +80 -46
  174. data/spec/praxis/media_type_identifier_spec.rb +13 -10
  175. data/spec/praxis/media_type_spec.rb +12 -12
  176. data/spec/praxis/middleware_app_spec.rb +23 -22
  177. data/spec/praxis/multipart/parser_spec.rb +7 -9
  178. data/spec/praxis/notifications_spec.rb +4 -4
  179. data/spec/praxis/plugin_concern_spec.rb +5 -6
  180. data/spec/praxis/renderer_spec.rb +10 -9
  181. data/spec/praxis/request_spec.rb +38 -41
  182. data/spec/praxis/request_stages/action_spec.rb +14 -15
  183. data/spec/praxis/request_stages/request_stage_spec.rb +30 -41
  184. data/spec/praxis/request_stages/validate_spec.rb +3 -1
  185. data/spec/praxis/response_definition_spec.rb +79 -92
  186. data/spec/praxis/response_spec.rb +35 -40
  187. data/spec/praxis/responses/internal_server_error_spec.rb +6 -9
  188. data/spec/praxis/responses/validation_error_spec.rb +17 -18
  189. data/spec/praxis/route_spec.rb +4 -7
  190. data/spec/praxis/router_spec.rb +69 -79
  191. data/spec/praxis/routing_config_spec.rb +15 -14
  192. data/spec/praxis/stage_spec.rb +56 -53
  193. data/spec/praxis/trait_spec.rb +17 -17
  194. data/spec/praxis/types/fuzzy_hash_spec.rb +11 -9
  195. data/spec/praxis/types/multipart_array/part_definition_spec.rb +3 -2
  196. data/spec/praxis/types/multipart_array_spec.rb +33 -48
  197. data/spec/spec_app/app/concerns/authenticated.rb +5 -5
  198. data/spec/spec_app/app/concerns/basic_api.rb +3 -1
  199. data/spec/spec_app/app/concerns/log_wrapper.rb +5 -3
  200. data/spec/spec_app/app/controllers/base_class.rb +6 -5
  201. data/spec/spec_app/app/controllers/instances.rb +31 -34
  202. data/spec/spec_app/app/controllers/volumes.rb +6 -6
  203. data/spec/spec_app/app/responses/multipart.rb +1 -2
  204. data/spec/spec_app/app/responses/other_response.rb +2 -2
  205. data/spec/spec_app/config/environment.rb +19 -6
  206. data/spec/spec_app/config.ru +4 -3
  207. data/spec/spec_app/design/api.rb +13 -15
  208. data/spec/spec_app/design/media_types/instance.rb +6 -6
  209. data/spec/spec_app/design/media_types/volume.rb +2 -1
  210. data/spec/spec_app/design/media_types/volume_snapshot.rb +2 -1
  211. data/spec/spec_app/design/resources/instances.rb +11 -17
  212. data/spec/spec_app/design/resources/volume_snapshots.rb +4 -5
  213. data/spec/spec_app/design/resources/volumes.rb +4 -5
  214. data/spec/spec_helper.rb +11 -13
  215. data/spec/support/be_deep_equal_matcher.rb +5 -0
  216. data/spec/support/spec_authorization_plugin.rb +7 -12
  217. data/spec/support/spec_blueprints.rb +5 -4
  218. data/spec/support/spec_complex_authentication_plugin.rb +17 -34
  219. data/spec/support/spec_endpoint_definitions.rb +2 -3
  220. data/spec/support/spec_media_types.rb +28 -35
  221. data/spec/support/spec_resources.rb +22 -16
  222. data/spec/support/spec_simple_authentication_plugin.rb +5 -9
  223. data/tasks/loader.thor +4 -2
  224. data/tasks/thor/app.rb +7 -5
  225. data/tasks/thor/example.rb +23 -22
  226. data/tasks/thor/model.rb +7 -7
  227. data/tasks/thor/scaffold.rb +23 -23
  228. data/tasks/thor/templates/generator/example_app/app/v1/resources/user.rb +0 -8
  229. data/tasks/thor/templates/generator/scaffold/implementation/resources/item.rb +1 -2
  230. metadata +72 -84
  231. data/MAINTAINERS +0 -2
  232. data/TODO.md +0 -25
  233. data/spec/praxis/api_resource_spec.rb +0 -0
  234. data/spec/praxis/dispatcher_spec.rb +0 -0
  235. data/spec/spec_app/app/responses/bulk_response.rb +0 -0
@@ -1,12 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  require 'praxis/extensions/field_selection'
4
6
 
5
7
  describe Praxis::Extensions::FieldSelection::FieldSelector do
6
-
7
8
  let(:type) { described_class.for(Address) }
8
9
 
9
-
10
10
  subject(:field_selector) { type.load(fields) }
11
11
 
12
12
  context '.example' do
@@ -21,7 +21,6 @@ describe Praxis::Extensions::FieldSelection::FieldSelector do
21
21
  it 'validates' do
22
22
  expect(type.example.validate).to be_empty
23
23
  end
24
-
25
24
  end
26
25
 
27
26
  context '.load' do
@@ -32,22 +31,20 @@ describe Praxis::Extensions::FieldSelection::FieldSelector do
32
31
  expect(type.load('').fields).to be(true)
33
32
  end
34
33
 
35
-
36
34
  it 'loads fields' do
37
35
  fields = 'id,name,owner{name}'
38
36
 
39
- expect(Attributor::FieldSelector).to receive(:load).
40
- with(fields).and_return(parsed_fields)
37
+ expect(Attributor::FieldSelector).to receive(:load)
38
+ .with(fields).and_return(parsed_fields)
41
39
 
42
40
  result = type.load(fields)
43
41
  expect(result.fields).to be parsed_fields
44
42
  end
45
-
46
43
  end
47
44
 
48
45
  context '#dump' do
49
46
  it 'is dumpable' do
50
- expect( type.load('id') ).to be_kind_of( Attributor::Dumpable )
47
+ expect(type.load('id')).to be_kind_of(Attributor::Dumpable)
51
48
  end
52
49
 
53
50
  it 'dumps nested fields properly' do
@@ -72,14 +69,13 @@ describe Praxis::Extensions::FieldSelection::FieldSelector do
72
69
  context 'validating subattributes' do
73
70
  let(:selector_string) { 'id,resident,owner{age}' }
74
71
  it 'validates subattributes' do
75
- errors = type.validate(selector_string)
72
+ errors = type.validate(selector_string)
76
73
  expect(errors).to match_array([
77
- "Attribute with name resident not found in Address",
78
- "Attribute with name age not found in Person"
79
- ])
74
+ 'Attribute with name resident not found in Address',
75
+ 'Attribute with name age not found in Person'
76
+ ])
80
77
  end
81
78
  end
82
-
83
79
  end
84
80
 
85
81
  context '#validate' do
@@ -90,7 +86,4 @@ describe Praxis::Extensions::FieldSelection::FieldSelector do
90
86
  expect(type.validate('id,owner{foo}')).to have(1).items
91
87
  end
92
88
  end
93
-
94
-
95
-
96
89
  end
@@ -1,46 +1,49 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
  require 'sequel'
3
5
 
4
6
  require 'praxis/extensions/field_selection/sequel_query_selector'
5
7
 
8
+ class QTest
9
+ attr_reader :object, :cols
6
10
 
7
- describe Praxis::Extensions::FieldSelection::SequelQuerySelector do
8
- class Q
9
- attr_reader :object, :cols
10
- def initialize
11
- @object = {}
12
- @cols = []
13
- end
14
- def eager(hash)
15
- raise "we are only calling eager one at a time!" if hash.keys.size > 1
16
- name = hash.keys.first
17
- # Actually call the incoming proc with an instance of Q, to collect the further select/eager calls
18
- @object[name] = hash[name].call(Q.new)
19
- self
20
- end
21
- def select(*names)
22
- @cols += names.map(&:column)
23
- self
24
- end
25
- def dump
26
- eagers = @object.each_with_object({}) do |(name, val), hash|
27
- hash[name] = val.dump
28
- end
29
- {
30
- columns: @cols,
31
- eagers: eagers
32
- }
33
- end
11
+ def initialize
12
+ @object = {}
13
+ @cols = []
14
+ end
15
+
16
+ def eager(hash)
17
+ raise 'we are only calling eager one at a time!' if hash.keys.size > 1
18
+
19
+ name = hash.keys.first
20
+ # Actually call the incoming proc with an instance of QTest, to collect the further select/eager calls
21
+ @object[name] = hash[name].call(QTest.new)
22
+ self
23
+ end
24
+
25
+ def select(*names)
26
+ @cols += names.map(&:column)
27
+ self
34
28
  end
35
29
 
30
+ def dump
31
+ eagers = @object.transform_values(&:dump)
32
+ {
33
+ columns: @cols,
34
+ eagers: eagers
35
+ }
36
+ end
37
+ end
36
38
 
39
+ describe Praxis::Extensions::FieldSelection::SequelQuerySelector do
37
40
  # Pay the price for creating and connecting only in this spec instead in spec helper
38
41
  # this way all other specs do not need to be slower and it's a better TDD experience
39
42
 
40
- require_relative '../support/spec_resources_sequel.rb'
41
-
43
+ require_relative '../support/spec_resources_sequel'
44
+
42
45
  let(:selector_fields) do
43
- {
46
+ {
44
47
  name: true,
45
48
  other_model: {
46
49
  id: true
@@ -64,15 +67,15 @@ describe Praxis::Extensions::FieldSelection::SequelQuerySelector do
64
67
  ]
65
68
  end
66
69
 
67
- let(:selector_node) { Praxis::Mapper::SelectorGenerator.new.add(SequelSimpleResource,selector_fields).selectors }
68
- subject {described_class.new(query: query, selectors: selector_node, debug: debug) }
70
+ let(:selector_node) { Praxis::Mapper::SelectorGenerator.new.add(SequelSimpleResource, selector_fields).selectors }
71
+ subject { described_class.new(query: query, selectors: selector_node, debug: debug) }
69
72
 
70
73
  context 'generate' do
71
74
  let(:debug) { false }
72
75
  context 'using the real models and DB' do
73
76
  let(:query) { SequelSimpleModel }
74
77
 
75
- it 'calls the select columns for the top level, and includes the right association hashes' do
78
+ it 'calls the select columns for the top level, and includes the right association hashes' do
76
79
  ds = subject.generate
77
80
  opts = ds.opts
78
81
  # Top model is our simplemodel
@@ -80,26 +83,26 @@ describe Praxis::Extensions::FieldSelection::SequelQuerySelector do
80
83
  selected_column_names = opts[:select].map(&:column)
81
84
  expect(selected_column_names).to match_array(expected_select_from_to_query)
82
85
  # 2 Eager loaded associations as well
83
- expect(opts[:eager].keys).to match_array([:other_model, :parent, :tags])
86
+ expect(opts[:eager].keys).to match_array(%i[other_model parent tags])
84
87
  # We can not introspect those eagers, as they are procs...but at least validate they are
85
88
  expect(opts[:eager][:other_model]).to be_a Proc
86
89
  expect(opts[:eager][:parent]).to be_a Proc
87
-
90
+
88
91
  # Also, let's make sure the query actually works by making Sequel attempt to retrieve it and finding the right things.
89
92
  result = ds.all
90
93
  # 2 simple models
91
- expect(result.size).to be 2
94
+ expect(result.size).to be 2
92
95
  # First simple model points to other_model 11 and parent 1
93
- simple_one = result.find{|i| i.id == 1}
96
+ simple_one = result.find { |i| i.id == 1 }
94
97
  expect(simple_one.other_model.id).to be 11
95
98
  expect(simple_one.parent.id).to be 1
96
99
  # also, its' parent in turn has 2 children (1 and 2) linked by its parent_uuid
97
- expect(simple_one.parent.children.map(&:id)).to match_array([1,2])
100
+ expect(simple_one.parent.children.map(&:id)).to match_array([1, 2])
98
101
  # Has the blue and red tags
99
- expect(simple_one.tags.map(&:tag_name)).to match_array(['blue','red'])
102
+ expect(simple_one.tags.map(&:tag_name)).to match_array(%w[blue red])
100
103
 
101
104
  # second simple model points to other_model 22 and parent 2
102
- simple_two = result.find{|i| i.id == 2}
105
+ simple_two = result.find { |i| i.id == 2 }
103
106
  expect(simple_two.other_model.id).to be 22
104
107
  expect(simple_two.parent.id).to be 2
105
108
  # also, its' parent in turn has no children (as no simple models point to it by uuid)
@@ -112,36 +115,34 @@ describe Praxis::Extensions::FieldSelection::SequelQuerySelector do
112
115
  # Actually make it run all the way...but suppressing the output
113
116
  subject.generate
114
117
  end
115
- end
118
+ end
116
119
  end
117
120
  context 'just mocking the query' do
118
- let(:query) { Q.new }
121
+ let(:query) { QTest.new }
119
122
 
120
- it 'creates the right recursive lambdas for the eager loading' do
121
-
123
+ it 'creates the right recursive lambdas for the eager loading' do
122
124
  ds = subject.generate
123
125
  result = ds.dump
124
126
  expect(result[:columns]).to match_array(expected_select_from_to_query)
125
127
  # 2 eager loads
126
- expect(result[:eagers].keys).to match_array([:other_model, :parent, :tags])
128
+ expect(result[:eagers].keys).to match_array(%i[other_model parent tags])
127
129
  # 1 - other model
128
130
  other_model_eagers = result[:eagers][:other_model]
129
131
  expect(other_model_eagers[:columns]).to match_array([:id])
130
132
 
131
133
  # 2 - parent association
132
134
  parent_eagers = result[:eagers][:parent]
133
- expect(parent_eagers[:columns]).to match_array([:id,:uuid]) # uuid is necessary for the "children" assoc
135
+ expect(parent_eagers[:columns]).to match_array(%i[id uuid]) # uuid is necessary for the "children" assoc
134
136
  expect(parent_eagers[:eagers].keys).to match_array([:children])
135
137
  # 2.1 - children association off of the parent
136
138
  parent_children_eagers = parent_eagers[:eagers][:children]
137
- expect(parent_children_eagers[:columns]).to match_array([:id,:parent_uuid]) # parent_uuid is required for the assoc
139
+ expect(parent_children_eagers[:columns]).to match_array(%i[id parent_uuid]) # parent_uuid is required for the assoc
138
140
  expect(parent_children_eagers[:eagers]).to be_empty
139
141
 
140
142
  # 3 - tags association
141
143
  tags_eagers = result[:eagers][:tags]
142
- expect(tags_eagers[:columns]).to match_array([:id, :tag_name]) # uuid is necessary for the "children" assoc
144
+ expect(tags_eagers[:columns]).to match_array(%i[id tag_name]) # uuid is necessary for the "children" assoc
143
145
  expect(tags_eagers[:eagers].keys).to be_empty
144
-
145
146
  end
146
147
  end
147
148
  end
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
- require_relative '../support/spec_resources_active_model.rb'
5
+ require_relative '../support/spec_resources_active_model'
4
6
  require 'praxis/extensions/pagination'
5
7
 
6
8
  class Book < Praxis::MediaType
@@ -15,7 +17,7 @@ BookPaginationParamsAttribute = Attributor::Attribute.new(Praxis::Types::Paginat
15
17
  max_items 3
16
18
  page_size 2
17
19
  # disallow :paging
18
- default by: :id
20
+ default by: :id
19
21
  end
20
22
 
21
23
  BookOrderingParamsAttribute = Attributor::Attribute.new(Praxis::Types::OrderingParams.for(Book)) do
@@ -49,10 +51,9 @@ describe Praxis::Extensions::Pagination::ActiveRecordPaginationHandler do
49
51
  Praxis::Extensions::Pagination::PaginationStruct.new(paginator_params, order_params)
50
52
  end
51
53
 
52
-
53
54
  context '.paginate' do
54
- subject {described_class.paginate(query, pagination) }
55
-
55
+ subject { described_class.paginate(query, pagination) }
56
+
56
57
  context 'empty struct' do
57
58
  let(:paginator_params) { nil }
58
59
 
@@ -63,68 +64,68 @@ describe Praxis::Extensions::Pagination::ActiveRecordPaginationHandler do
63
64
 
64
65
  context 'page-based' do
65
66
  it_behaves_like 'paginates_the_same', 'page=1,items=3',
66
- ::ActiveBook.limit(3)
67
+ ::ActiveBook.limit(3)
67
68
  it_behaves_like 'paginates_the_same', 'page=2,items=3',
68
- ::ActiveBook.offset(3).limit(3)
69
+ ::ActiveBook.offset(3).limit(3)
69
70
  end
70
71
 
71
72
  context 'page-based with defaults' do
72
73
  it_behaves_like 'paginates_the_same', '',
73
- ::ActiveBook.offset(0).limit(2)
74
+ ::ActiveBook.offset(0).limit(2)
74
75
  it_behaves_like 'paginates_the_same', 'page=2',
75
- ::ActiveBook.offset(2).limit(2)
76
+ ::ActiveBook.offset(2).limit(2)
76
77
  end
77
78
 
78
79
  context 'cursor-based' do
79
80
  it_behaves_like 'paginates_the_same', 'by=id,items=3',
80
- ::ActiveBook.limit(3).order(id: :asc)
81
+ ::ActiveBook.limit(3).order(id: :asc)
81
82
  it_behaves_like 'paginates_the_same', 'by=id,from=1000,items=3',
82
- ::ActiveBook.where("id > 1000").limit(3).order(id: :asc)
83
+ ::ActiveBook.where('id > 1000').limit(3).order(id: :asc)
83
84
  it_behaves_like 'paginates_the_same', 'by=simple_name,from=Book1000,items=3',
84
- ::ActiveBook.where("simple_name > 'Book1000'").limit(3).order(simple_name: :asc)
85
+ ::ActiveBook.where("simple_name > 'Book1000'").limit(3).order(simple_name: :asc)
85
86
  end
86
87
 
87
88
  context 'cursor-based with defaults' do
88
89
  it_behaves_like 'paginates_the_same', '',
89
- ::ActiveBook.limit(2).order(id: :asc)
90
+ ::ActiveBook.limit(2).order(id: :asc)
90
91
  it_behaves_like 'paginates_the_same', 'by=id,from=1000',
91
- ::ActiveBook.where("id > 1000").limit(2).order(id: :asc)
92
+ ::ActiveBook.where('id > 1000').limit(2).order(id: :asc)
92
93
  end
93
94
 
94
95
  context 'including order' do
95
96
  let(:order_params) { BookOrderingParamsAttribute.load(op_string) }
96
97
 
97
98
  context 'when compatible with cursor' do
98
- let(:op_string){ 'id'}
99
+ let(:op_string) { 'id' }
99
100
  # Compatible cursor field
100
101
  it_behaves_like 'paginates_the_same', 'by=id,items=3',
101
- ::ActiveBook.limit(3).order(id: :asc)
102
+ ::ActiveBook.limit(3).order(id: :asc)
102
103
  end
103
-
104
+
104
105
  context 'when incompatible with cursor' do
105
- let(:op_string){ 'id'}
106
+ let(:op_string) { 'id' }
106
107
  let(:paginator_params) { BookPaginationParamsAttribute.load('by=simple_name,items=3') }
107
- it do
108
- expect{subject.all}.to raise_error(described_class::PaginationException, /is incompatible with pagination/)
108
+ it do
109
+ expect { subject.all }.to raise_error(described_class::PaginationException, /is incompatible with pagination/)
109
110
  end
110
111
  end
111
112
  end
112
113
  end
113
114
 
114
- context '.order' do
115
- subject {described_class.order(query, pagination.order) }
116
-
115
+ context '.order' do
116
+ subject { described_class.order(query, pagination.order) }
117
+
117
118
  it 'does not change the query with an empty struct' do
118
119
  expect(subject).to be(query)
119
120
  end
120
121
 
121
- it_behaves_like 'sorts_the_same', 'simple_name',
122
- ::ActiveBook.order(simple_name: :asc)
123
- it_behaves_like 'sorts_the_same', '-simple_name',
124
- ::ActiveBook.order(simple_name: :desc)
125
- it_behaves_like 'sorts_the_same', '-simple_name,id',
126
- ::ActiveBook.order(simple_name: :desc, id: :asc)
127
- it_behaves_like 'sorts_the_same', '-simple_name,-id',
128
- ::ActiveBook.order(simple_name: :desc, id: :desc)
122
+ it_behaves_like 'sorts_the_same', 'simple_name',
123
+ ::ActiveBook.order(simple_name: :asc)
124
+ it_behaves_like 'sorts_the_same', '-simple_name',
125
+ ::ActiveBook.order(simple_name: :desc)
126
+ it_behaves_like 'sorts_the_same', '-simple_name,id',
127
+ ::ActiveBook.order(simple_name: :desc, id: :asc)
128
+ it_behaves_like 'sorts_the_same', '-simple_name,-id',
129
+ ::ActiveBook.order(simple_name: :desc, id: :desc)
129
130
  end
130
131
  end
@@ -1,19 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Praxis::Extensions::Rendering do
4
-
5
6
  let(:test_class) do
6
- Struct.new(:media_type, :expanded_fields, :response, :request) do |klass|
7
+ Struct.new(:media_type, :expanded_fields, :response, :request) do |_klass|
7
8
  include Praxis::Extensions::Rendering
8
9
  end
9
10
  end
10
11
 
11
12
  let(:media_type) { Person }
12
- let(:expanded_fields) { {id: true, name: true} }
13
+ let(:expanded_fields) { { id: true, name: true } }
13
14
  let(:response) { double('response', headers: {}) }
14
15
  let(:request) { double('request') }
15
16
 
16
- let(:object) { {id: '1', name: 'bob', href: '/people/bob'} }
17
+ let(:object) { { id: '1', name: 'bob', href: '/people/bob' } }
17
18
  subject(:instance) { test_class.new(media_type, expanded_fields, response, request) }
18
19
 
19
20
  context '#render' do
@@ -26,7 +27,7 @@ describe Praxis::Extensions::Rendering do
26
27
  context '#display' do
27
28
  context 'without exception' do
28
29
  before do
29
- expect(response).to receive(:body=).with({id: 1, name: 'bob'})
30
+ expect(response).to receive(:body=).with({ id: 1, name: 'bob' })
30
31
  end
31
32
 
32
33
  subject!(:output) { instance.display(object) }
@@ -43,15 +44,15 @@ describe Praxis::Extensions::Rendering do
43
44
  context 'with a rendering exception' do
44
45
  let(:handler_params) do
45
46
  {
46
- summary: "Circular Rendering Error when rendering response. " +
47
- "Please especify a view to narrow the dependent fields, or narrow your field set.",
47
+ summary: 'Circular Rendering Error when rendering response. ' \
48
+ 'Please especify a view to narrow the dependent fields, or narrow your field set.',
48
49
  exception: circular_exception,
49
50
  request: request,
50
51
  stage: :action,
51
52
  errors: nil
52
53
  }
53
54
  end
54
- let(:circular_exception){ Praxis::Renderer::CircularRenderingError.new(object, "ctx") }
55
+ let(:circular_exception) { Praxis::Renderer::CircularRenderingError.new(object, 'ctx') }
55
56
  it 'catches a circular rendering exception' do
56
57
  expect(instance).to receive(:render).and_raise(circular_exception)
57
58
  expect(Praxis::Application.instance.validation_handler).to receive(:handle!).with(handler_params)
@@ -59,5 +60,4 @@ describe Praxis::Extensions::Rendering do
59
60
  end
60
61
  end
61
62
  end
62
-
63
63
  end
@@ -1,16 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_record'
2
4
 
3
5
  require 'praxis/mapper/active_model_compat'
4
6
 
5
7
  # Creates a new in-memory DB, and the necessary tables (and mini-seeds) for the models in this file
6
8
  def create_tables
7
-
8
9
  ActiveRecord::Base.establish_connection(
9
- adapter: 'sqlite3',
10
- dbfile: ':memory:',
11
- database: ':memory:'
10
+ adapter: 'sqlite3',
11
+ dbfile: ':memory:',
12
+ database: ':memory:'
12
13
  )
13
-
14
+
14
15
  ActiveRecord::Schema.define do
15
16
  ActiveRecord::Migration.suppress_messages do
16
17
  create_table :active_books do |table|
@@ -19,20 +20,20 @@ def create_tables
19
20
  table.column :category_uuid, :string
20
21
  table.column :author_id, :integer
21
22
  end
22
-
23
+
23
24
  create_table :active_authors do |table|
24
25
  table.column :name, :string
25
26
  end
26
-
27
+
27
28
  create_table :active_categories do |table|
28
29
  table.column :uuid, :string
29
30
  table.column :name, :string
30
31
  end
31
-
32
+
32
33
  create_table :active_tags do |table|
33
34
  table.column :name, :string
34
35
  end
35
-
36
+
36
37
  create_table :active_taggings do |table|
37
38
  table.column :book_id, :integer
38
39
  table.column :tag_id, :integer
@@ -51,7 +52,7 @@ class ActiveBook < ActiveRecord::Base
51
52
  belongs_to :author, class_name: 'ActiveAuthor'
52
53
 
53
54
  has_many :taggings, class_name: 'ActiveTagging', foreign_key: :book_id
54
- has_many :primary_taggings, lambda { where(label: 'primary')}, class_name: 'ActiveTagging', foreign_key: :book_id
55
+ has_many :primary_taggings, -> { where(label: 'primary') }, class_name: 'ActiveTagging', foreign_key: :book_id
55
56
 
56
57
  has_many :tags, class_name: 'ActiveTag', through: :taggings
57
58
  has_many :primary_tags, class_name: 'ActiveTag', through: :primary_taggings, source: :tag
@@ -79,7 +80,6 @@ class ActiveTagging < ActiveRecord::Base
79
80
  belongs_to :tag, class_name: 'ActiveTag', foreign_key: :tag_id
80
81
  end
81
82
 
82
-
83
83
  # A set of resource classes for use in specs
84
84
  class ActiveBaseResource < Praxis::Mapper::Resource
85
85
  end
@@ -102,63 +102,47 @@ class ActiveBookResource < ActiveBaseResource
102
102
  model ActiveBook
103
103
 
104
104
  filters_mapping(
105
- id: :id,
106
- # category_uuid: :category_uuid #NOTE: we do not need to define same-name-mappings if they exist
107
105
  'fake_nested.name': 'simple_name',
108
106
  'name': 'simple_name',
109
107
  'name_is_not': lambda do |spec| # Silly way to use a proc, but good enough for testing
110
- { name: :simple_name, value: spec[:value] , op: '!=' } # Can be an array for multiple conditions as well
111
- end,
112
- 'author.id': 'author.id',
113
- 'author.name': 'author.name',
114
- 'taggings.label': 'taggings.label',
115
- 'taggings.tag_id': 'taggings.tag_id',
116
- 'taggings.tag.taggings.tag_id': 'taggings.tag.taggings.tag_id',
117
- 'tags.name': 'tags.name',
118
- 'primary_tags.name': 'primary_tags.name',
119
- 'category.name': 'category.name',
120
- 'category.books.name': 'category.books.simple_name',
121
- 'category.books.taggings.tag_id': 'category.books.taggings.tag_id',
122
- 'category.books.taggings.label': 'category.books.taggings.label',
123
- 'primary_tags': 'primary_tags',
124
- 'category.books.taggings': 'category.books.taggings',
108
+ { name: :simple_name, value: spec[:value], op: '!=' } # Can be an array for multiple conditions as well
109
+ end,
110
+ 'category.books.name': 'category.books.simple_name'
125
111
  )
126
112
  # Forces to add an extra column (added_column)...and yet another (author_id) that will serve
127
113
  # to check that if that's already automatically added due to an association, it won't interfere or duplicate
128
- property :author, dependencies: [:author, :added_column, :author_id]
114
+ property :author, dependencies: %i[author added_column author_id]
129
115
 
130
116
  property :name, dependencies: [:simple_name]
131
117
  end
132
118
 
133
-
134
119
  def seed_data
135
- cat1 = ActiveCategory.create( id: 1 , uuid: 'deadbeef1', name: 'cat1' )
136
- cat2 = ActiveCategory.create( id: 2 , uuid: 'deadbeef2', name: 'cat2' )
137
-
138
- author1 = ActiveAuthor.create( id: 11, name: 'author1' )
139
- author2 = ActiveAuthor.create( id: 22, name: 'author2' )
140
- author3 = ActiveAuthor.create( id: 33, name: nil )
141
-
142
- tag_blue = ActiveTag.create(id: 1 , name: 'blue' )
143
- tag_red = ActiveTag.create(id: 2 , name: 'red' )
144
- tag_green = ActiveTag.create(id: 3 , name: 'green' )
145
-
146
- book1 = ActiveBook.create( id: 1 , simple_name: 'Book1', category_uuid: 'deadbeef1')
120
+ cat1 = ActiveCategory.create(id: 1, uuid: 'deadbeef1', name: 'cat1')
121
+ cat2 = ActiveCategory.create(id: 2, uuid: 'deadbeef2', name: 'cat2')
122
+
123
+ author1 = ActiveAuthor.create(id: 11, name: 'author1')
124
+ author2 = ActiveAuthor.create(id: 22, name: 'author2')
125
+ author3 = ActiveAuthor.create(id: 33, name: nil)
126
+
127
+ tag_blue = ActiveTag.create(id: 1, name: 'blue')
128
+ tag_red = ActiveTag.create(id: 2, name: 'red')
129
+ tag_green = ActiveTag.create(id: 3, name: 'green')
130
+
131
+ book1 = ActiveBook.create(id: 1, simple_name: 'Book1', category_uuid: 'deadbeef1')
147
132
  book1.author = author1
148
133
  book1.category = cat1
149
134
  book1.save
150
135
  ActiveTagging.create(book: book1, tag: tag_blue, label: 'primary')
151
136
  ActiveTagging.create(book: book1, tag: tag_red)
152
137
  ActiveTagging.create(book: book1, tag: tag_green, label: 'primary')
153
-
154
138
 
155
- book2 = ActiveBook.create( id: 2 , simple_name: 'Book2', category_uuid: 'deadbeef1')
139
+ book2 = ActiveBook.create(id: 2, simple_name: 'Book2', category_uuid: 'deadbeef1')
156
140
  book2.author = author2
157
141
  book2.category = cat2
158
142
  book2.save
159
143
  ActiveTagging.create(book: book2, tag: tag_red, label: 'primary')
160
144
 
161
- book3 = ActiveBook.create( id: 3 , simple_name: 'Book3', category_uuid: 'deadbeef1')
145
+ book3 = ActiveBook.create(id: 3, simple_name: 'Book3', category_uuid: 'deadbeef1')
162
146
  book3.author = author3
163
147
  book3.save
164
148
  ActiveTagging.create(book: book3, tag: tag_red, label: 'primary')
@@ -166,10 +150,9 @@ def seed_data
166
150
  # More stuff
167
151
 
168
152
  10.times do |i|
169
- bid = 1000+i
170
- ActiveBook.create( id: bid , simple_name: "Book#{bid}")
153
+ bid = 1000 + i
154
+ ActiveBook.create(id: bid, simple_name: "Book#{bid}")
171
155
  end
172
-
173
156
  end
174
157
 
175
- seed_data
158
+ seed_data