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.
- checksums.yaml +4 -4
- data/.rubocop.yml +54 -0
- data/.simplecov +3 -1
- data/.travis.yml +2 -1
- data/CHANGELOG.md +19 -0
- data/CONTRIBUTING.md +2 -79
- data/Gemfile +5 -1
- data/Guardfile +6 -4
- data/LICENSE +0 -2
- data/MAINTAINERS.md +1 -0
- data/README.md +15 -22
- data/Rakefile +4 -2
- data/bin/praxis +55 -58
- data/lib/praxis/action_definition/headers_dsl_compiler.rb +5 -6
- data/lib/praxis/action_definition.rb +65 -95
- data/lib/praxis/api_definition.rb +21 -29
- data/lib/praxis/api_general_info.rb +55 -66
- data/lib/praxis/application.rb +15 -32
- data/lib/praxis/blueprint.rb +80 -73
- data/lib/praxis/bootloader.rb +24 -33
- data/lib/praxis/bootloader_stages/environment.rb +5 -10
- data/lib/praxis/bootloader_stages/file_loader.rb +3 -6
- data/lib/praxis/bootloader_stages/plugin_config_load.rb +4 -6
- data/lib/praxis/bootloader_stages/plugin_config_prepare.rb +2 -2
- data/lib/praxis/bootloader_stages/plugin_loader.rb +3 -7
- data/lib/praxis/bootloader_stages/plugin_setup.rb +3 -3
- data/lib/praxis/bootloader_stages/routing.rb +5 -8
- data/lib/praxis/bootloader_stages/subgroup_loader.rb +2 -10
- data/lib/praxis/bootloader_stages/warn_unloaded_files.rb +15 -19
- data/lib/praxis/callbacks.rb +12 -11
- data/lib/praxis/collection.rb +11 -14
- data/lib/praxis/config.rb +17 -28
- data/lib/praxis/config_hash.rb +2 -1
- data/lib/praxis/controller.rb +7 -6
- data/lib/praxis/dispatcher.rb +34 -42
- data/lib/praxis/docs/open_api/info_object.rb +11 -8
- data/lib/praxis/docs/open_api/media_type_object.rb +18 -17
- data/lib/praxis/docs/open_api/operation_object.rb +7 -4
- data/lib/praxis/docs/open_api/parameter_object.rb +17 -14
- data/lib/praxis/docs/open_api/paths_object.rb +11 -9
- data/lib/praxis/docs/open_api/request_body_object.rb +14 -13
- data/lib/praxis/docs/open_api/response_object.rb +24 -18
- data/lib/praxis/docs/open_api/responses_object.rb +3 -1
- data/lib/praxis/docs/open_api/schema_object.rb +61 -29
- data/lib/praxis/docs/open_api/server_object.rb +5 -2
- data/lib/praxis/docs/open_api/tag_object.rb +9 -6
- data/lib/praxis/docs/open_api_generator.rb +114 -150
- data/lib/praxis/endpoint_definition.rb +60 -77
- data/lib/praxis/error_handler.rb +2 -2
- data/lib/praxis/exception.rb +2 -0
- data/lib/praxis/exceptions/config.rb +3 -1
- data/lib/praxis/exceptions/config_load.rb +2 -0
- data/lib/praxis/exceptions/config_validation.rb +3 -1
- data/lib/praxis/exceptions/invalid_configuration.rb +3 -1
- data/lib/praxis/exceptions/invalid_response.rb +3 -1
- data/lib/praxis/exceptions/invalid_trait.rb +3 -1
- data/lib/praxis/exceptions/stage_not_found.rb +3 -1
- data/lib/praxis/exceptions/validation.rb +4 -3
- data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +163 -149
- data/lib/praxis/extensions/attribute_filtering/active_record_patches/5x.rb +18 -13
- data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_0.rb +13 -9
- data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_1_plus.rb +14 -11
- data/lib/praxis/extensions/attribute_filtering/active_record_patches.rb +12 -9
- data/lib/praxis/extensions/attribute_filtering/filter_tree_node.rb +8 -5
- data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +89 -65
- data/lib/praxis/extensions/attribute_filtering/filters_parser.rb +68 -62
- data/lib/praxis/extensions/attribute_filtering.rb +3 -1
- data/lib/praxis/extensions/field_expansion.rb +6 -4
- data/lib/praxis/extensions/field_selection/active_record_query_selector.rb +10 -8
- data/lib/praxis/extensions/field_selection/field_selector.rb +91 -92
- data/lib/praxis/extensions/field_selection/sequel_query_selector.rb +12 -12
- data/lib/praxis/extensions/field_selection.rb +3 -1
- data/lib/praxis/extensions/pagination/active_record_pagination_handler.rb +6 -4
- data/lib/praxis/extensions/pagination/header_generator.rb +16 -11
- data/lib/praxis/extensions/pagination/ordering_params.rb +29 -28
- data/lib/praxis/extensions/pagination/pagination_handler.rb +44 -42
- data/lib/praxis/extensions/pagination/pagination_params.rb +29 -48
- data/lib/praxis/extensions/pagination/sequel_pagination_handler.rb +8 -7
- data/lib/praxis/extensions/pagination.rb +10 -15
- data/lib/praxis/extensions/rails_compat/request_methods.rb +3 -4
- data/lib/praxis/extensions/rails_compat.rb +2 -0
- data/lib/praxis/extensions/rendering.rb +12 -12
- data/lib/praxis/field_expander.rb +8 -9
- data/lib/praxis/file_group.rb +8 -12
- data/lib/praxis/finalizable.rb +1 -0
- data/lib/praxis/handlers/json.rb +5 -2
- data/lib/praxis/handlers/plain.rb +2 -1
- data/lib/praxis/handlers/www_form.rb +6 -3
- data/lib/praxis/handlers/{xml-sample.rb → xml_sample.rb} +26 -22
- data/lib/praxis/mapper/active_model_compat.rb +13 -10
- data/lib/praxis/mapper/resource.rb +196 -181
- data/lib/praxis/mapper/selector_generator.rb +106 -112
- data/lib/praxis/mapper/sequel_compat.rb +70 -67
- data/lib/praxis/media_type.rb +2 -2
- data/lib/praxis/media_type_identifier.rb +26 -22
- data/lib/praxis/middleware_app.rb +18 -15
- data/lib/praxis/multipart/parser.rb +46 -51
- data/lib/praxis/multipart/part.rb +78 -110
- data/lib/praxis/notifications.rb +2 -4
- data/lib/praxis/plugin.rb +11 -18
- data/lib/praxis/plugin_concern.rb +12 -15
- data/lib/praxis/plugins/mapper_plugin.rb +15 -13
- data/lib/praxis/plugins/pagination_plugin.rb +8 -6
- data/lib/praxis/plugins/rails_plugin.rb +33 -28
- data/lib/praxis/renderer.rb +11 -15
- data/lib/praxis/request.rb +48 -44
- data/lib/praxis/request_stages/action.rb +4 -6
- data/lib/praxis/request_stages/load_request.rb +2 -4
- data/lib/praxis/request_stages/request_stage.rb +19 -23
- data/lib/praxis/request_stages/response.rb +4 -6
- data/lib/praxis/request_stages/validate.rb +3 -5
- data/lib/praxis/request_stages/validate_params_and_headers.rb +15 -22
- data/lib/praxis/request_stages/validate_payload.rb +25 -28
- data/lib/praxis/request_superclassing.rb +3 -3
- data/lib/praxis/resource_definition.rb +1 -0
- data/lib/praxis/response.rb +24 -26
- data/lib/praxis/response_definition.rb +77 -122
- data/lib/praxis/response_template.rb +11 -15
- data/lib/praxis/responses/http.rb +23 -44
- data/lib/praxis/responses/internal_server_error.rb +18 -21
- data/lib/praxis/responses/multipart_ok.rb +4 -9
- data/lib/praxis/responses/validation_error.rb +8 -15
- data/lib/praxis/route.rb +8 -10
- data/lib/praxis/router/rack.rb +13 -7
- data/lib/praxis/router/simple.rb +10 -5
- data/lib/praxis/router.rb +27 -34
- data/lib/praxis/routing_config.rb +52 -29
- data/lib/praxis/simple_media_type.rb +5 -8
- data/lib/praxis/stage.rb +17 -25
- data/lib/praxis/tasks/api_docs.rb +17 -16
- data/lib/praxis/tasks/console.rb +3 -1
- data/lib/praxis/tasks/environment.rb +2 -0
- data/lib/praxis/tasks/routes.rb +26 -24
- data/lib/praxis/tasks.rb +3 -1
- data/lib/praxis/trait.rb +37 -46
- data/lib/praxis/types/fuzzy_hash.rb +13 -14
- data/lib/praxis/types/media_type_common.rb +11 -10
- data/lib/praxis/types/multipart_array/part_definition.rb +14 -17
- data/lib/praxis/types/multipart_array.rb +100 -115
- data/lib/praxis/validation_handler.rb +5 -3
- data/lib/praxis/version.rb +3 -1
- data/lib/praxis.rb +4 -5
- data/praxis.gemspec +22 -21
- data/spec/functional_spec.rb +44 -56
- data/spec/praxis/action_definition_spec.rb +39 -48
- data/spec/praxis/api_definition_spec.rb +45 -47
- data/spec/praxis/api_general_info_spec.rb +28 -29
- data/spec/praxis/application_spec.rb +18 -14
- data/spec/praxis/blueprint_spec.rb +33 -34
- data/spec/praxis/bootloader_spec.rb +32 -30
- data/spec/praxis/callbacks_spec.rb +37 -37
- data/spec/praxis/collection_spec.rb +18 -25
- data/spec/praxis/config_hash_spec.rb +5 -4
- data/spec/praxis/config_spec.rb +27 -26
- data/spec/praxis/controller_spec.rb +8 -9
- data/spec/praxis/endpoint_definition_spec.rb +25 -32
- data/spec/praxis/extensions/attribute_filtering/active_record_filter_query_builder_spec.rb +171 -114
- data/spec/praxis/extensions/attribute_filtering/filter_tree_node_spec.rb +22 -21
- data/spec/praxis/extensions/attribute_filtering/filtering_params_spec.rb +112 -60
- data/spec/praxis/extensions/attribute_filtering/filters_parser_spec.rb +37 -38
- data/spec/praxis/extensions/field_expansion_spec.rb +8 -10
- data/spec/praxis/extensions/field_selection/active_record_query_selector_spec.rb +14 -13
- data/spec/praxis/extensions/field_selection/field_selector_spec.rb +9 -16
- data/spec/praxis/extensions/field_selection/sequel_query_selector_spec.rb +50 -49
- data/spec/praxis/extensions/pagination/active_record_pagination_handler_spec.rb +32 -31
- data/spec/praxis/extensions/rendering_spec.rb +9 -9
- data/spec/praxis/extensions/support/spec_resources_active_model.rb +32 -49
- data/spec/praxis/extensions/support/spec_resources_sequel.rb +48 -48
- data/spec/praxis/field_expander_spec.rb +6 -5
- data/spec/praxis/file_group_spec.rb +3 -1
- data/spec/praxis/handlers/json_spec.rb +6 -5
- data/spec/praxis/mapper/resource_spec.rb +39 -29
- data/spec/praxis/mapper/selector_generator_spec.rb +80 -46
- data/spec/praxis/media_type_identifier_spec.rb +13 -10
- data/spec/praxis/media_type_spec.rb +12 -12
- data/spec/praxis/middleware_app_spec.rb +23 -22
- data/spec/praxis/multipart/parser_spec.rb +7 -9
- data/spec/praxis/notifications_spec.rb +4 -4
- data/spec/praxis/plugin_concern_spec.rb +5 -6
- data/spec/praxis/renderer_spec.rb +10 -9
- data/spec/praxis/request_spec.rb +38 -41
- data/spec/praxis/request_stages/action_spec.rb +14 -15
- data/spec/praxis/request_stages/request_stage_spec.rb +30 -41
- data/spec/praxis/request_stages/validate_spec.rb +3 -1
- data/spec/praxis/response_definition_spec.rb +79 -92
- data/spec/praxis/response_spec.rb +35 -40
- data/spec/praxis/responses/internal_server_error_spec.rb +6 -9
- data/spec/praxis/responses/validation_error_spec.rb +17 -18
- data/spec/praxis/route_spec.rb +4 -7
- data/spec/praxis/router_spec.rb +69 -79
- data/spec/praxis/routing_config_spec.rb +15 -14
- data/spec/praxis/stage_spec.rb +56 -53
- data/spec/praxis/trait_spec.rb +17 -17
- data/spec/praxis/types/fuzzy_hash_spec.rb +11 -9
- data/spec/praxis/types/multipart_array/part_definition_spec.rb +3 -2
- data/spec/praxis/types/multipart_array_spec.rb +33 -48
- data/spec/spec_app/app/concerns/authenticated.rb +5 -5
- data/spec/spec_app/app/concerns/basic_api.rb +3 -1
- data/spec/spec_app/app/concerns/log_wrapper.rb +5 -3
- data/spec/spec_app/app/controllers/base_class.rb +6 -5
- data/spec/spec_app/app/controllers/instances.rb +31 -34
- data/spec/spec_app/app/controllers/volumes.rb +6 -6
- data/spec/spec_app/app/responses/multipart.rb +1 -2
- data/spec/spec_app/app/responses/other_response.rb +2 -2
- data/spec/spec_app/config/environment.rb +19 -6
- data/spec/spec_app/config.ru +4 -3
- data/spec/spec_app/design/api.rb +13 -15
- data/spec/spec_app/design/media_types/instance.rb +6 -6
- data/spec/spec_app/design/media_types/volume.rb +2 -1
- data/spec/spec_app/design/media_types/volume_snapshot.rb +2 -1
- data/spec/spec_app/design/resources/instances.rb +11 -17
- data/spec/spec_app/design/resources/volume_snapshots.rb +4 -5
- data/spec/spec_app/design/resources/volumes.rb +4 -5
- data/spec/spec_helper.rb +11 -13
- data/spec/support/be_deep_equal_matcher.rb +5 -0
- data/spec/support/spec_authorization_plugin.rb +7 -12
- data/spec/support/spec_blueprints.rb +5 -4
- data/spec/support/spec_complex_authentication_plugin.rb +17 -34
- data/spec/support/spec_endpoint_definitions.rb +2 -3
- data/spec/support/spec_media_types.rb +28 -35
- data/spec/support/spec_resources.rb +22 -16
- data/spec/support/spec_simple_authentication_plugin.rb +5 -9
- data/tasks/loader.thor +4 -2
- data/tasks/thor/app.rb +7 -5
- data/tasks/thor/example.rb +23 -22
- data/tasks/thor/model.rb +7 -7
- data/tasks/thor/scaffold.rb +23 -23
- data/tasks/thor/templates/generator/example_app/app/v1/resources/user.rb +0 -8
- data/tasks/thor/templates/generator/scaffold/implementation/resources/item.rb +1 -2
- metadata +72 -84
- data/MAINTAINERS +0 -2
- data/TODO.md +0 -25
- data/spec/praxis/api_resource_spec.rb +0 -0
- data/spec/praxis/dispatcher_spec.rb +0 -0
- data/spec/spec_app/app/responses/bulk_response.rb +0 -0
@@ -1,21 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
require 'praxis/extensions/attribute_filtering'
|
4
6
|
|
5
7
|
describe Praxis::Extensions::AttributeFiltering::FilteringParams do
|
6
|
-
|
7
8
|
context '.load' do
|
8
9
|
subject { described_class.load(filters_string) }
|
9
10
|
|
10
11
|
context 'unescapes the URL encoded values' do
|
11
12
|
it 'for single values' do
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
str = "one=#{CGI.escape('*')}&two>#{CGI.escape('^%$#!st_uff')}|three<normal"
|
14
|
+
parsed = [
|
15
|
+
{ name: :one, op: '=', value: '*' },
|
16
|
+
{ name: :two, op: '>', value: '^%$#!st_uff' },
|
17
|
+
{ name: :three, op: '<', value: 'normal' }
|
18
|
+
]
|
19
|
+
expect(described_class.load(str).parsed_array.map { |i| i.slice(:name, :op, :value) }).to eq(parsed)
|
19
20
|
end
|
20
21
|
it 'each of the multi-values' do
|
21
22
|
escaped_one = [
|
@@ -25,55 +26,55 @@ describe Praxis::Extensions::AttributeFiltering::FilteringParams do
|
|
25
26
|
].join(',')
|
26
27
|
str = "one=#{escaped_one}&two>normal"
|
27
28
|
parsed = [
|
28
|
-
{ name: :one, op: '=', value: ['fun!','Times','~!@#$%^&*()_+-={}|[]\:";\'<>?,./`']},
|
29
|
-
{ name: :two, op: '>', value: 'normal'}
|
29
|
+
{ name: :one, op: '=', value: ['fun!', 'Times', '~!@#$%^&*()_+-={}|[]\:";\'<>?,./`'] },
|
30
|
+
{ name: :two, op: '>', value: 'normal' }
|
30
31
|
]
|
31
|
-
expect(described_class.load(str).parsed_array.map{|i| i.slice(:name
|
32
|
+
expect(described_class.load(str).parsed_array.map { |i| i.slice(:name, :op, :value) }).to eq(parsed)
|
32
33
|
end
|
33
34
|
it 'does not handle badly escaped values that contain reserved chars ()|&,' do
|
34
35
|
badly_escaped = 'val('
|
35
36
|
str = "one=#{badly_escaped}&(two>normal|three!)"
|
36
|
-
expect
|
37
|
+
expect do
|
37
38
|
described_class.load(str)
|
38
|
-
|
39
|
+
end.to raise_error(Parslet::ParseFailed)
|
39
40
|
end
|
40
41
|
end
|
41
42
|
context 'parses for operator' do
|
42
43
|
described_class::VALUE_OPERATORS.each do |op|
|
43
|
-
it
|
44
|
+
it op.to_s do
|
44
45
|
str = "thename#{op}thevalue"
|
45
|
-
parsed = [{ name: :thename, op: op, value: 'thevalue'}]
|
46
|
-
expect(described_class.load(str).parsed_array.map{|i| i.slice(:name
|
46
|
+
parsed = [{ name: :thename, op: op, value: 'thevalue' }]
|
47
|
+
expect(described_class.load(str).parsed_array.map { |i| i.slice(:name, :op, :value) }).to eq(parsed)
|
47
48
|
end
|
48
49
|
end
|
49
50
|
described_class::NOVALUE_OPERATORS.each do |op|
|
50
|
-
it
|
51
|
+
it op.to_s do
|
51
52
|
str = "thename#{op}"
|
52
|
-
parsed = [{ name: :thename, op: op, value: nil}]
|
53
|
-
expect(described_class.load(str).parsed_array.map{|i| i.slice(:name
|
53
|
+
parsed = [{ name: :thename, op: op, value: nil }]
|
54
|
+
expect(described_class.load(str).parsed_array.map { |i| i.slice(:name, :op, :value) }).to eq(parsed)
|
54
55
|
end
|
55
56
|
end
|
56
57
|
it 'can parse multiple values for filter' do
|
57
|
-
str=
|
58
|
-
parsed = [{ name: :filtername, op: '=', value: [
|
59
|
-
expect(described_class.load(str).parsed_array.map{|i| i.slice(:name
|
58
|
+
str = 'filtername=1,2,3'
|
59
|
+
parsed = [{ name: :filtername, op: '=', value: %w[1 2 3] }]
|
60
|
+
expect(described_class.load(str).parsed_array.map { |i| i.slice(:name, :op, :value) }).to eq(parsed)
|
60
61
|
end
|
61
62
|
end
|
62
63
|
context 'with all value operators at once for the same AND group' do
|
63
|
-
let(:filters_string) { 'one=11&two!=22&three>=33&four<=4&five<5&six>6&seven!&eight!!'}
|
64
|
+
let(:filters_string) { 'one=11&two!=22&three>=33&four<=4&five<5&six>6&seven!&eight!!' }
|
64
65
|
it do
|
65
|
-
expect(subject.parsed_array.map{|i| i.slice(:name
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
66
|
+
expect(subject.parsed_array.map { |i| i.slice(:name, :op, :value) }).to eq([
|
67
|
+
{ name: :one, op: '=', value: '11' },
|
68
|
+
{ name: :two, op: '!=', value: '22' },
|
69
|
+
{ name: :three, op: '>=', value: '33' },
|
70
|
+
{ name: :four, op: '<=', value: '4' },
|
71
|
+
{ name: :five, op: '<', value: '5' },
|
72
|
+
{ name: :six, op: '>', value: '6' },
|
73
|
+
{ name: :seven, op: '!', value: nil },
|
74
|
+
{ name: :eight, op: '!!', value: nil }
|
75
|
+
])
|
75
76
|
# And all have the same parent, which is an AND group
|
76
|
-
parent = subject.parsed_array.map{|i|i[:node_object].parent_group}.uniq
|
77
|
+
parent = subject.parsed_array.map { |i| i[:node_object].parent_group }.uniq
|
77
78
|
expect(parent.size).to eq(1)
|
78
79
|
expect(parent.first.type).to eq(:and)
|
79
80
|
expect(parent.first.parent_group).to be_nil
|
@@ -81,17 +82,17 @@ describe Praxis::Extensions::AttributeFiltering::FilteringParams do
|
|
81
82
|
end
|
82
83
|
|
83
84
|
context 'with with nested precedence groups' do
|
84
|
-
let(:filters_string) { '(one=11)&(two!=22|three!!)&four<=4&five>5|six!'}
|
85
|
+
let(:filters_string) { '(one=11)&(two!=22|three!!)&four<=4&five>5|six!' }
|
85
86
|
it do
|
86
87
|
parsed = subject.parsed_array
|
87
|
-
expect(parsed.map{|i| i.slice(:name
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
88
|
+
expect(parsed.map { |i| i.slice(:name, :op, :value) }).to eq([
|
89
|
+
{ name: :one, op: '=', value: '11' },
|
90
|
+
{ name: :two, op: '!=', value: '22' },
|
91
|
+
{ name: :three, op: '!!', value: nil },
|
92
|
+
{ name: :four, op: '<=', value: '4' },
|
93
|
+
{ name: :five, op: '>', value: '5' },
|
94
|
+
{ name: :six, op: '!', value: nil }
|
95
|
+
])
|
95
96
|
# Grouped appropriately
|
96
97
|
parent_of = parsed.each_with_object({}) do |item, hash|
|
97
98
|
hash[item[:name]] = item[:node_object].parent_group
|
@@ -106,9 +107,9 @@ describe Praxis::Extensions::AttributeFiltering::FilteringParams do
|
|
106
107
|
# two and 3 are grouped together by an OR
|
107
108
|
expect(parent_of[:two]).to be(parent_of[:three])
|
108
109
|
expect(parent_of[:two].type).to eq(:or)
|
109
|
-
|
110
|
+
|
110
111
|
# one, two, four and the or from two/three are grouped together by an AND
|
111
|
-
expect([parent_of[:one],parent_of[:two].parent_group,parent_of[:four],parent_of[:five]]).to all(be(parent_of[:one]))
|
112
|
+
expect([parent_of[:one], parent_of[:two].parent_group, parent_of[:four], parent_of[:five]]).to all(be(parent_of[:one]))
|
112
113
|
expect(parent_of[:one].type).to eq(:and)
|
113
114
|
|
114
115
|
# six and the whole group above are grouped together with an OR
|
@@ -119,11 +120,12 @@ describe Praxis::Extensions::AttributeFiltering::FilteringParams do
|
|
119
120
|
|
120
121
|
context 'value coercing when associated to a MediaType' do
|
121
122
|
let(:parsed) do
|
122
|
-
# Note wrap the filter_params (.for) type in an attribute (which then we discard), so it will
|
123
|
-
# construct it propertly by applying the block. Seems easier than creating the type alone, and
|
123
|
+
# Note wrap the filter_params (.for) type in an attribute (which then we discard), so it will
|
124
|
+
# construct it propertly by applying the block. Seems easier than creating the type alone, and
|
124
125
|
# then manually apply the block
|
125
126
|
Attributor::Attribute.new(described_class.for(Post)) do
|
126
127
|
filter 'id', using: ['=', '!=', '!']
|
128
|
+
any 'updated_at', using: ['>', '<', '=']
|
127
129
|
end.type.load(str).parsed_array
|
128
130
|
end
|
129
131
|
|
@@ -151,19 +153,41 @@ describe Praxis::Extensions::AttributeFiltering::FilteringParams do
|
|
151
153
|
expect(parsed.first[:value]).to be_nil
|
152
154
|
end
|
153
155
|
end
|
154
|
-
end
|
155
156
|
|
157
|
+
context 'for an ANY matcher' do
|
158
|
+
let(:str) { 'blog.timestamps.updated_at>=2001-01-01' }
|
159
|
+
it 'coerces its value to the associated mediatype attribute type' do
|
160
|
+
expect(parsed.first[:value]).to eq(DateTime.parse('2001-01-01'))
|
161
|
+
nested_updated_at_attr = Post.attributes[:blog].attributes[:timestamps].attributes[:updated_at]
|
162
|
+
expect(nested_updated_at_attr.type.valid_type?(parsed.first[:value])).to be_truthy
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
context 'for both normal and ANY type filters' do
|
167
|
+
let(:str) { 'blog.timestamps.updated_at>=2001-01-01&id=42' }
|
168
|
+
it 'properly parsed them both, and into their own types' do
|
169
|
+
expect(parsed.first[:value]).to eq(DateTime.parse('2001-01-01'))
|
170
|
+
nested_updated_at_attr = Post.attributes[:blog].attributes[:timestamps].attributes[:updated_at]
|
171
|
+
expect(nested_updated_at_attr.type.valid_type?(parsed.first[:value])).to be_truthy
|
172
|
+
|
173
|
+
expect(parsed.second[:value]).to eq(42)
|
174
|
+
id_attr = Post.attributes[:blog].attributes[:id]
|
175
|
+
expect(id_attr.type.valid_type?(parsed.second[:value])).to be_truthy
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
156
179
|
end
|
157
180
|
|
158
181
|
context '.validate' do
|
159
182
|
let(:filtering_params_type) do
|
160
|
-
# Note wrap the filter_params (.for) type in an attribute (which then we discard), so it will
|
161
|
-
# construct it propertly by applying the block. Seems easier than creating the type alone, and
|
183
|
+
# Note wrap the filter_params (.for) type in an attribute (which then we discard), so it will
|
184
|
+
# construct it propertly by applying the block. Seems easier than creating the type alone, and
|
162
185
|
# then manually apply the block
|
163
186
|
Attributor::Attribute.new(described_class.for(Post)) do
|
164
187
|
filter 'id', using: ['=', '!=', '!']
|
165
188
|
filter 'title', using: ['=', '!='], fuzzy: true
|
166
189
|
filter 'content', using: ['=', '!=', '!']
|
190
|
+
any 'updated_at', using: ['>', '<', '=']
|
167
191
|
end.type
|
168
192
|
end
|
169
193
|
let(:loaded_params) { filtering_params_type.load(filters_string) }
|
@@ -171,23 +195,23 @@ describe Praxis::Extensions::AttributeFiltering::FilteringParams do
|
|
171
195
|
|
172
196
|
context 'errors' do
|
173
197
|
context 'given attributes that do not exist in the type' do
|
174
|
-
let(:filters_string) { 'NotAnExistingAttribute=Foobar*'}
|
198
|
+
let(:filters_string) { 'NotAnExistingAttribute=Foobar*' }
|
175
199
|
it 'raises an error' do
|
176
|
-
expect{subject}.to raise_error(/NotAnExistingAttribute.*does not exist/)
|
200
|
+
expect { subject }.to raise_error(/NotAnExistingAttribute.*does not exist/)
|
177
201
|
end
|
178
202
|
end
|
179
203
|
|
180
204
|
context 'given unallowed attributes' do
|
181
|
-
let(:filters_string) { 'href=Foobar*'}
|
205
|
+
let(:filters_string) { 'href=Foobar*' }
|
182
206
|
it 'raises an error' do
|
183
207
|
expect(subject).to_not be_empty
|
184
|
-
matches_error = subject.any? {|err| err =~ /Filtering by href is not allowed/}
|
208
|
+
matches_error = subject.any? { |err| err =~ /Filtering by href is not allowed.*leaf attributes matching updated_at/ }
|
185
209
|
expect(matches_error).to be_truthy
|
186
210
|
end
|
187
211
|
end
|
188
212
|
|
189
213
|
context 'given unallowed operator' do
|
190
|
-
let(:filters_string) { 'title>Foobar*'}
|
214
|
+
let(:filters_string) { 'title>Foobar*' }
|
191
215
|
it 'raises an error' do
|
192
216
|
expect(subject).to_not be_empty
|
193
217
|
expect(subject.first).to match(/Operator > not allowed for filter title/)
|
@@ -195,15 +219,43 @@ describe Praxis::Extensions::AttributeFiltering::FilteringParams do
|
|
195
219
|
end
|
196
220
|
end
|
197
221
|
|
222
|
+
context 'for any-type leaf filters' do
|
223
|
+
context 'with the correct operator' do
|
224
|
+
let(:filters_string) { 'timestamps.updated_at>2001-01-01' }
|
225
|
+
it 'succeeds' do
|
226
|
+
expect(subject).to be_empty
|
227
|
+
end
|
228
|
+
end
|
229
|
+
context 'with the correct operator on a nested mediatype' do
|
230
|
+
let(:filters_string) { 'blog.timestamps.updated_at>2001-01-01' }
|
231
|
+
it 'succeeds' do
|
232
|
+
expect(subject).to be_empty
|
233
|
+
end
|
234
|
+
end
|
235
|
+
context 'with an incorrect operator' do
|
236
|
+
let(:filters_string) { 'blog.timestamps.updated_at>=2001-01-01' }
|
237
|
+
it 'complains about it' do
|
238
|
+
expect(subject).to_not be_empty
|
239
|
+
expect(subject.first).to match(/Operator >= not allowed for filter blog.timestamps.updated_at/)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
context 'with an correct operator but passing an unexistent attribute path' do
|
243
|
+
let(:filters_string) { 'some.not.true.path.updated_at>=2001-01-01' }
|
244
|
+
it 'complains about it' do
|
245
|
+
expect { subject }.to raise_error(/Error, you've requested to filter by field 'some'/)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
198
250
|
context 'non-valued operators' do
|
199
251
|
context 'for string typed fields' do
|
200
|
-
let(:filters_string) { 'content!'}
|
252
|
+
let(:filters_string) { 'content!' }
|
201
253
|
it 'validates properly' do
|
202
254
|
expect(subject).to be_empty
|
203
255
|
end
|
204
256
|
end
|
205
257
|
context 'for non-string typed fields' do
|
206
|
-
let(:filters_string) { 'id!'}
|
258
|
+
let(:filters_string) { 'id!' }
|
207
259
|
it 'validates properly' do
|
208
260
|
expect(subject).to be_empty
|
209
261
|
end
|
@@ -212,7 +264,7 @@ describe Praxis::Extensions::AttributeFiltering::FilteringParams do
|
|
212
264
|
context 'fuzzy matches' do
|
213
265
|
context 'when allowed' do
|
214
266
|
context 'given a fuzzy string' do
|
215
|
-
let(:filters_string) { 'title=IAmAString*'}
|
267
|
+
let(:filters_string) { 'title=IAmAString*' }
|
216
268
|
it 'validates properly' do
|
217
269
|
expect(subject).to be_empty
|
218
270
|
end
|
@@ -220,14 +272,14 @@ describe Praxis::Extensions::AttributeFiltering::FilteringParams do
|
|
220
272
|
end
|
221
273
|
context 'when NOT allowed' do
|
222
274
|
context 'given a fuzzy string' do
|
223
|
-
let(:filters_string) { 'content=IAmAString*'}
|
275
|
+
let(:filters_string) { 'content=IAmAString*' }
|
224
276
|
it 'errors out' do
|
225
277
|
expect(subject).to_not be_empty
|
226
278
|
expect(subject.first).to match(/Fuzzy matching for content is not allowed/)
|
227
279
|
end
|
228
280
|
end
|
229
281
|
context 'given a non-fuzzy string' do
|
230
|
-
let(:filters_string) { 'content=IAmAString'}
|
282
|
+
let(:filters_string) { 'content=IAmAString' }
|
231
283
|
it 'validates properly' do
|
232
284
|
expect(subject).to be_empty
|
233
285
|
end
|
@@ -1,22 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'praxis/extensions/attribute_filtering/filters_parser'
|
2
4
|
|
3
5
|
describe Praxis::Extensions::AttributeFiltering::FilteringParams::Condition do
|
4
|
-
end
|
6
|
+
end
|
5
7
|
|
6
8
|
describe Praxis::Extensions::AttributeFiltering::FilteringParams::ConditionGroup do
|
7
|
-
end
|
9
|
+
end
|
8
10
|
|
9
11
|
describe Praxis::Extensions::AttributeFiltering::FilteringParams::Parser do
|
10
|
-
|
11
12
|
context 'testing' do
|
12
13
|
let(:expectations) do
|
13
|
-
{
|
14
|
-
'one=11|two=22' =>
|
14
|
+
{
|
15
|
+
'one=11|two=22' => '( one=11 OR two=22 )'
|
15
16
|
}
|
16
17
|
end
|
17
18
|
it 'parses and loads the parsed result into the tree objects' do
|
18
19
|
expectations.each do |filters, dump_result|
|
19
|
-
|
20
20
|
parsed = described_class.new.parse(filters)
|
21
21
|
tree = Praxis::Extensions::AttributeFiltering::FilteringParams::ConditionGroup.load(parsed)
|
22
22
|
|
@@ -24,8 +24,7 @@ describe Praxis::Extensions::AttributeFiltering::FilteringParams::Parser do
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
27
|
-
|
28
|
-
|
27
|
+
context 'parses the grammar' do
|
29
28
|
# Takes a hash with keys containing literal filters string, and values being the "dump format for Condition/Group"
|
30
29
|
shared_examples 'round-trip-properly' do |expectations|
|
31
30
|
it description do
|
@@ -38,24 +37,24 @@ describe Praxis::Extensions::AttributeFiltering::FilteringParams::Parser do
|
|
38
37
|
end
|
39
38
|
|
40
39
|
context 'single expression' do
|
41
|
-
it_behaves_like 'round-trip-properly', {
|
40
|
+
it_behaves_like 'round-trip-properly', {
|
42
41
|
'one=11' => 'one=11',
|
43
42
|
'(one=11)' => 'one=11',
|
44
|
-
'one!' =>
|
43
|
+
'one!' => 'one!'
|
45
44
|
}
|
46
45
|
end
|
47
46
|
context 'same expression operator' do
|
48
|
-
it_behaves_like 'round-trip-properly', {
|
47
|
+
it_behaves_like 'round-trip-properly', {
|
49
48
|
'one=11&two=22' => '( one=11 AND two=22 )',
|
50
49
|
'one=11&two=22&three=3' => '( one=11 AND two=22 AND three=3 )',
|
51
50
|
'one=1,2,3&two=4,5' => '( one=[1,2,3] AND two=[4,5] )',
|
52
51
|
'one=11|two=22' => '( one=11 OR two=22 )',
|
53
|
-
'one=11|two=22|three=3' => '( one=11 OR two=22 OR three=3 )'
|
52
|
+
'one=11|two=22|three=3' => '( one=11 OR two=22 OR three=3 )'
|
54
53
|
}
|
55
54
|
end
|
56
55
|
|
57
56
|
context 'respects and/or precedence and parenthesis grouping' do
|
58
|
-
it_behaves_like 'round-trip-properly', {
|
57
|
+
it_behaves_like 'round-trip-properly', {
|
59
58
|
'a=1&b=2&z=9|c=3' => '( ( a=1 AND b=2 AND z=9 ) OR c=3 )',
|
60
59
|
'a=1|b=2&c=3' => '( a=1 OR ( b=2 AND c=3 ) )',
|
61
60
|
'a=1|b=2&c=3&d=4' => '( a=1 OR ( b=2 AND c=3 AND d=4 ) )',
|
@@ -65,56 +64,56 @@ describe Praxis::Extensions::AttributeFiltering::FilteringParams::Parser do
|
|
65
64
|
'one=11&two=2|three=3' => '( ( one=11 AND two=2 ) OR three=3 )', # AND has higer precedence
|
66
65
|
'one=11|two=2&three=3' => '( one=11 OR ( two=2 AND three=3 ) )', # AND has higer precedence
|
67
66
|
'one=11&two=2|three=3&four=4' => '( ( one=11 AND two=2 ) OR ( three=3 AND four=4 ) )',
|
68
|
-
'(one=11)&(two!=2|three=3)&four=4&five=5|six=6' =>
|
67
|
+
'(one=11)&(two!=2|three=3)&four=4&five=5|six=6' =>
|
69
68
|
'( ( one=11 AND ( two!=2 OR three=3 ) AND four=4 AND five=5 ) OR six=6 )',
|
70
69
|
'(one=11)&three=3' => '( one=11 AND three=3 )',
|
71
70
|
'(one=11|two=2)&(three=3|four=4)' => '( ( one=11 OR two=2 ) AND ( three=3 OR four=4 ) )',
|
72
71
|
'(category_uuid=deadbeef1|category_uuid=deadbeef2)&(name=Book1|name=Book2)' =>
|
73
72
|
'( ( category_uuid=deadbeef1 OR category_uuid=deadbeef2 ) AND ( name=Book1 OR name=Book2 ) )',
|
74
73
|
'(category_uuid=deadbeef1&name=Book1)|(category_uuid=deadbeef2&name=Book2)' =>
|
75
|
-
'( ( category_uuid=deadbeef1 AND name=Book1 ) OR ( category_uuid=deadbeef2 AND name=Book2 ) )'
|
74
|
+
'( ( category_uuid=deadbeef1 AND name=Book1 ) OR ( category_uuid=deadbeef2 AND name=Book2 ) )'
|
76
75
|
}
|
77
76
|
end
|
78
77
|
|
79
78
|
context 'empty values get converted to empty strings' do
|
80
|
-
it_behaves_like 'round-trip-properly', {
|
79
|
+
it_behaves_like 'round-trip-properly', {
|
81
80
|
'one=' => 'one=""',
|
82
|
-
'one=&two=2' => '( one="" AND two=2 )'
|
81
|
+
'one=&two=2' => '( one="" AND two=2 )'
|
83
82
|
}
|
84
83
|
end
|
85
84
|
|
86
85
|
context 'no value operands' do
|
87
|
-
it_behaves_like 'round-trip-properly', {
|
88
|
-
'one!' =>
|
89
|
-
'one!!' =>
|
86
|
+
it_behaves_like 'round-trip-properly', {
|
87
|
+
'one!' => 'one!',
|
88
|
+
'one!!' => 'one!!'
|
90
89
|
}
|
91
90
|
|
92
91
|
it 'fails if passing a value' do
|
93
|
-
expect
|
92
|
+
expect do
|
94
93
|
described_class.new.parse('one!val')
|
95
|
-
|
96
|
-
expect
|
94
|
+
end.to raise_error(Parslet::ParseFailed)
|
95
|
+
expect do
|
97
96
|
described_class.new.parse('one!!val')
|
98
|
-
|
99
|
-
end
|
97
|
+
end.to raise_error(Parslet::ParseFailed)
|
98
|
+
end
|
100
99
|
end
|
101
100
|
|
102
101
|
context 'csv values result in multiple values for the operation' do
|
103
|
-
it_behaves_like 'round-trip-properly', {
|
104
|
-
'multi=1,2' =>
|
105
|
-
'multi=1,2,valuehere' =>
|
102
|
+
it_behaves_like 'round-trip-properly', {
|
103
|
+
'multi=1,2' => 'multi=[1,2]',
|
104
|
+
'multi=1,2,valuehere' => 'multi=[1,2,valuehere]'
|
106
105
|
}
|
107
106
|
end
|
108
107
|
|
109
108
|
context 'supports [a-zA-Z0-9_\.] for filter names' do
|
110
|
-
it_behaves_like 'round-trip-properly', {
|
111
|
-
'normal=1'
|
112
|
-
'cOmBo=1'
|
113
|
-
'1=2'
|
109
|
+
it_behaves_like 'round-trip-properly', {
|
110
|
+
'normal=1' => 'normal=1',
|
111
|
+
'cOmBo=1' => 'cOmBo=1',
|
112
|
+
'1=2' => '1=2',
|
114
113
|
'aFew42Things=1' => 'aFew42Things=1',
|
115
114
|
'under_scores=1' => 'under_scores=1',
|
116
115
|
'several.dots.in.here=1' => 'several.dots.in.here=1',
|
117
|
-
'cOrN.00copia.of_thinGs.42_here=1' => 'cOrN.00copia.of_thinGs.42_here=1'
|
116
|
+
'cOrN.00copia.of_thinGs.42_here=1' => 'cOrN.00copia.of_thinGs.42_here=1'
|
118
117
|
}
|
119
118
|
end
|
120
119
|
context 'supports everything (except &|(),) for values (even without encoding..not allowed, but just to ensure the parser does not bomb)' do
|
@@ -123,7 +122,7 @@ describe Praxis::Extensions::AttributeFiltering::FilteringParams::Parser do
|
|
123
122
|
'v=*foo*' => 'v={*}foo{*}',
|
124
123
|
'v=*^%$#@!foo' => 'v={*}^%$#@!foo',
|
125
124
|
'v=_-=\{}"?:><' => 'v=_-=\{}"?:><',
|
126
|
-
'v=_-=\{}"?:><,another_value!' => 'v=[_-=\{}"?:><,another_value!]'
|
125
|
+
'v=_-=\{}"?:><,another_value!' => 'v=[_-=\{}"?:><,another_value!]'
|
127
126
|
}
|
128
127
|
end
|
129
128
|
context 'properly detects and handles fuzzy matching encoded as {*} in the dump' do
|
@@ -132,7 +131,7 @@ describe Praxis::Extensions::AttributeFiltering::FilteringParams::Parser do
|
|
132
131
|
'v=*foo*' => 'v={*}foo{*}',
|
133
132
|
'v=foo*' => 'v=foo{*}',
|
134
133
|
'v=*start,end*,*both*' => 'v=[{*}start,end{*},{*}both{*}]',
|
135
|
-
"v=*#{CGI.escape('***')},#{CGI.escape('*')}" => 'v=[{*}***,*]'
|
134
|
+
"v=*#{CGI.escape('***')},#{CGI.escape('*')}" => 'v=[{*}***,*]' # Simple exact match on 2nd
|
136
135
|
}
|
137
136
|
end
|
138
137
|
context 'properly handles url-encoded values' do
|
@@ -141,8 +140,8 @@ describe Praxis::Extensions::AttributeFiltering::FilteringParams::Parser do
|
|
141
140
|
"v=*#{CGI.escape('foo')}*" => 'v={*}foo{*}',
|
142
141
|
"v=*#{CGI.escape('^%$#@!foo')}" => 'v={*}^%$#@!foo',
|
143
142
|
"v=#{CGI.escape('~!@#$%^&*()_+-={}|[]\:";\'<>?,./`')}" => 'v=~!@#$%^&*()_+-={}|[]\:";\'<>?,./`',
|
144
|
-
"v=#{CGI.escape('_-+=\{}"?:><')},#{CGI.escape('another_value!')}" => 'v=[_-+=\{}"?:><,another_value!]'
|
143
|
+
"v=#{CGI.escape('_-+=\{}"?:><')},#{CGI.escape('another_value!')}" => 'v=[_-+=\{}"?:><,another_value!]'
|
145
144
|
}
|
146
|
-
end
|
145
|
+
end
|
147
146
|
end
|
148
|
-
end
|
147
|
+
end
|
@@ -1,9 +1,10 @@
|
|
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::FieldExpansion do
|
6
|
-
|
7
8
|
# include the ActionDefinitionExtension module directly, as that's where the
|
8
9
|
# bulk of lies, and by including this instead of the base FieldExpansion module
|
9
10
|
# we avoid the side-effect of injecting the ActionDefinitionExtension into
|
@@ -24,8 +25,7 @@ describe Praxis::Extensions::FieldExpansion do
|
|
24
25
|
|
25
26
|
let(:request_params) do
|
26
27
|
double('params',
|
27
|
-
|
28
|
-
)
|
28
|
+
fields: Praxis::Extensions::FieldSelection::FieldSelector.for(Person).load(fields))
|
29
29
|
end
|
30
30
|
|
31
31
|
let(:request) { double('Praxis::Request', params: request_params) }
|
@@ -33,26 +33,25 @@ describe Praxis::Extensions::FieldExpansion do
|
|
33
33
|
|
34
34
|
let(:fields) { nil }
|
35
35
|
|
36
|
-
let(:test_attributes) {
|
36
|
+
let(:test_attributes) {}
|
37
37
|
let(:test_params) { double('test_params', attributes: test_attributes) }
|
38
38
|
|
39
|
-
subject(:expansion) { test_instance.expanded_fields(request, media_type)}
|
39
|
+
subject(:expansion) { test_instance.expanded_fields(request, media_type) }
|
40
40
|
|
41
41
|
context '#expanded_fields' do
|
42
|
-
|
43
42
|
context 'with fields and view params defined' do
|
44
43
|
let(:test_attributes) { {} }
|
45
44
|
|
46
45
|
context 'with no fields provided' do
|
47
46
|
it 'returns the fields for the default view' do
|
48
|
-
expect(expansion).to eq({id: true, name: true})
|
47
|
+
expect(expansion).to eq({ id: true, name: true })
|
49
48
|
end
|
50
49
|
end
|
51
50
|
|
52
51
|
context 'with a set of fields provided' do
|
53
52
|
let(:fields) { 'id,name,owner{name}' }
|
54
53
|
it 'returns the subset of fields' do
|
55
|
-
expected = {id: true, name: true }
|
54
|
+
expected = { id: true, name: true }
|
56
55
|
expect(expansion).to eq expected
|
57
56
|
end
|
58
57
|
end
|
@@ -61,9 +60,8 @@ describe Praxis::Extensions::FieldExpansion do
|
|
61
60
|
context 'with an action with no params' do
|
62
61
|
let(:test_params) { nil }
|
63
62
|
it 'ignores incoming parameters and expands for the default fieldset' do
|
64
|
-
expect(expansion).to eq({id: true, name: true})
|
63
|
+
expect(expansion).to eq({ id: true, name: true })
|
65
64
|
end
|
66
65
|
end
|
67
66
|
end
|
68
|
-
|
69
67
|
end
|
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
|
-
require_relative '../support/spec_resources_active_model
|
5
|
+
require_relative '../support/spec_resources_active_model'
|
4
6
|
|
5
7
|
describe Praxis::Extensions::FieldSelection::ActiveRecordQuerySelector do
|
6
8
|
let(:selector_fields) do
|
7
|
-
{
|
9
|
+
{
|
8
10
|
name: true,
|
9
11
|
author: {
|
10
12
|
id: true,
|
@@ -29,13 +31,13 @@ describe Praxis::Extensions::FieldSelection::ActiveRecordQuerySelector do
|
|
29
31
|
:id # We always load the primary keys
|
30
32
|
]
|
31
33
|
end
|
32
|
-
let(:selector_node) { Praxis::Mapper::SelectorGenerator.new.add(ActiveBookResource,selector_fields).selectors
|
33
|
-
let(:debug){ false }
|
34
|
+
let(:selector_node) { Praxis::Mapper::SelectorGenerator.new.add(ActiveBookResource, selector_fields).selectors }
|
35
|
+
let(:debug) { false }
|
34
36
|
|
35
|
-
subject(:selector) {described_class.new(query: query, selectors: selector_node, debug: debug) }
|
37
|
+
subject(:selector) { described_class.new(query: query, selectors: selector_node, debug: debug) }
|
36
38
|
context '#generate with a mocked' do
|
37
|
-
let(:query) { double(
|
38
|
-
it 'calls the select columns for the top level, and includes the right association hashes' do
|
39
|
+
let(:query) { double('Query') }
|
40
|
+
it 'calls the select columns for the top level, and includes the right association hashes' do
|
39
41
|
expect(query).to receive(:select).with(*expected_select_from_to_query).and_return(query)
|
40
42
|
expected_includes = {
|
41
43
|
author: {
|
@@ -49,9 +51,9 @@ describe Praxis::Extensions::FieldSelection::ActiveRecordQuerySelector do
|
|
49
51
|
expect(query).to receive(:includes).with(expected_includes).and_return(query)
|
50
52
|
expect(subject).to_not receive(:explain_query)
|
51
53
|
subject.generate
|
52
|
-
end
|
54
|
+
end
|
53
55
|
context 'when debug is enabled' do
|
54
|
-
let(:debug){ true }
|
56
|
+
let(:debug) { true }
|
55
57
|
it 'calls the explain method' do
|
56
58
|
expect(query).to receive(:select).and_return(query)
|
57
59
|
expect(query).to receive(:includes).and_return(query)
|
@@ -74,7 +76,7 @@ describe Praxis::Extensions::FieldSelection::ActiveRecordQuerySelector do
|
|
74
76
|
},
|
75
77
|
tags: {}
|
76
78
|
}
|
77
|
-
#expect(query).to receive(:includes).with(expected_includes).and_return(query)
|
79
|
+
# expect(query).to receive(:includes).with(expected_includes).and_return(query)
|
78
80
|
expect(subject).to_not receive(:explain_query)
|
79
81
|
final_query = subject.generate
|
80
82
|
expect(final_query.select_values).to match_array(expected_select_from_to_query)
|
@@ -90,7 +92,7 @@ describe Praxis::Extensions::FieldSelection::ActiveRecordQuerySelector do
|
|
90
92
|
expect(book1.author.books.size).to eq 1
|
91
93
|
expect(book1.author.books.map(&:simple_name)).to eq(['Book1'])
|
92
94
|
expect(book1.category.name).to eq 'cat1'
|
93
|
-
expect(book1.tags.map(&:name)).to match_array([
|
95
|
+
expect(book1.tags.map(&:name)).to match_array(%w[blue red green])
|
94
96
|
|
95
97
|
expect(book2.author.id).to eq 22
|
96
98
|
expect(book2.author.books.size).to eq 1
|
@@ -104,7 +106,6 @@ describe Praxis::Extensions::FieldSelection::ActiveRecordQuerySelector do
|
|
104
106
|
# Actually make it run all the way...but suppressing the output
|
105
107
|
subject.generate
|
106
108
|
end
|
107
|
-
end
|
109
|
+
end
|
108
110
|
end
|
109
|
-
|
110
111
|
end
|