praxis 2.0.pre.1 → 2.0.pre.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +0 -1
- data/.ruby-version +1 -0
- data/.travis.yml +5 -20
- data/CHANGELOG.md +32 -0
- data/Gemfile +1 -1
- data/Guardfile +2 -1
- data/Rakefile +1 -7
- data/TODO.md +28 -0
- data/lib/api_browser/package-lock.json +7110 -0
- data/lib/praxis.rb +7 -4
- data/lib/praxis/action_definition.rb +10 -17
- data/lib/praxis/action_definition/headers_dsl_compiler.rb +1 -1
- data/lib/praxis/api_general_info.rb +21 -0
- data/lib/praxis/application.rb +2 -3
- data/lib/praxis/bootloader_stages/routing.rb +2 -4
- data/lib/praxis/config.rb +1 -1
- data/lib/praxis/docs/generator.rb +11 -6
- data/lib/praxis/docs/open_api_generator.rb +255 -0
- data/lib/praxis/docs/openapi/info_object.rb +31 -0
- data/lib/praxis/docs/openapi/media_type_object.rb +59 -0
- data/lib/praxis/docs/openapi/operation_object.rb +40 -0
- data/lib/praxis/docs/openapi/parameter_object.rb +69 -0
- data/lib/praxis/docs/openapi/paths_object.rb +58 -0
- data/lib/praxis/docs/openapi/request_body_object.rb +51 -0
- data/lib/praxis/docs/openapi/response_object.rb +63 -0
- data/lib/praxis/docs/openapi/responses_object.rb +44 -0
- data/lib/praxis/docs/openapi/schema_object.rb +87 -0
- data/lib/praxis/docs/openapi/server_object.rb +24 -0
- data/lib/praxis/docs/openapi/tag_object.rb +21 -0
- data/lib/praxis/extensions/attribute_filtering.rb +2 -0
- data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +148 -157
- data/lib/praxis/extensions/attribute_filtering/active_record_patches.rb +15 -0
- data/lib/praxis/extensions/attribute_filtering/active_record_patches/5x.rb +90 -0
- data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_0.rb +68 -0
- data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_1_plus.rb +58 -0
- data/lib/praxis/extensions/attribute_filtering/filter_tree_node.rb +35 -0
- data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +13 -12
- data/lib/praxis/extensions/attribute_filtering/sequel_filter_query_builder.rb +3 -2
- data/lib/praxis/extensions/field_selection/active_record_query_selector.rb +24 -30
- data/lib/praxis/extensions/field_selection/field_selector.rb +4 -0
- data/lib/praxis/extensions/field_selection/sequel_query_selector.rb +32 -39
- data/lib/praxis/extensions/pagination.rb +130 -0
- data/lib/praxis/extensions/pagination/active_record_pagination_handler.rb +42 -0
- data/lib/praxis/extensions/pagination/header_generator.rb +70 -0
- data/lib/praxis/extensions/pagination/ordering_params.rb +234 -0
- data/lib/praxis/extensions/pagination/pagination_handler.rb +68 -0
- data/lib/praxis/extensions/pagination/pagination_params.rb +374 -0
- data/lib/praxis/extensions/pagination/sequel_pagination_handler.rb +45 -0
- data/lib/praxis/handlers/json.rb +2 -0
- data/lib/praxis/handlers/www_form.rb +5 -0
- data/lib/praxis/handlers/{xml.rb → xml-sample.rb} +6 -0
- data/lib/praxis/links.rb +4 -0
- data/lib/praxis/mapper/active_model_compat.rb +57 -4
- data/lib/praxis/mapper/resource.rb +18 -11
- data/lib/praxis/mapper/selector_generator.rb +99 -75
- data/lib/praxis/mapper/sequel_compat.rb +43 -3
- data/lib/praxis/media_type.rb +1 -56
- data/lib/praxis/media_type_identifier.rb +2 -1
- data/lib/praxis/multipart/part.rb +5 -2
- data/lib/praxis/notifications.rb +1 -1
- data/lib/praxis/plugins/mapper_plugin.rb +17 -3
- data/lib/praxis/plugins/pagination_plugin.rb +71 -0
- data/lib/praxis/resource_definition.rb +8 -16
- data/lib/praxis/response_definition.rb +1 -1
- data/lib/praxis/route.rb +3 -5
- data/lib/praxis/routing_config.rb +3 -7
- data/lib/praxis/tasks/api_docs.rb +23 -0
- data/lib/praxis/tasks/routes.rb +9 -14
- data/lib/praxis/trait.rb +1 -1
- data/lib/praxis/types/media_type_common.rb +12 -2
- data/lib/praxis/types/multipart.rb +1 -1
- data/lib/praxis/types/multipart_array.rb +64 -2
- data/lib/praxis/types/multipart_array/part_definition.rb +1 -1
- data/lib/praxis/validation_handler.rb +1 -2
- data/lib/praxis/version.rb +1 -1
- data/praxis.gemspec +11 -9
- data/spec/functional_spec.rb +9 -6
- data/spec/praxis/action_definition_spec.rb +4 -16
- data/spec/praxis/api_general_info_spec.rb +6 -6
- data/spec/praxis/application_spec.rb +1 -1
- data/spec/praxis/collection_spec.rb +3 -2
- data/spec/praxis/config_spec.rb +2 -2
- data/spec/praxis/extensions/attribute_filtering/active_record_filter_query_builder_spec.rb +304 -0
- data/spec/praxis/extensions/attribute_filtering/filter_tree_node_spec.rb +39 -0
- data/spec/praxis/extensions/attribute_filtering/filtering_params_spec.rb +34 -0
- data/spec/praxis/extensions/field_expansion_spec.rb +6 -24
- data/spec/praxis/extensions/field_selection/active_record_query_selector_spec.rb +110 -0
- data/spec/praxis/extensions/field_selection/sequel_query_selector_spec.rb +148 -0
- data/spec/praxis/extensions/pagination/active_record_pagination_handler_spec.rb +130 -0
- data/spec/praxis/extensions/support/spec_resources_active_model.rb +173 -0
- data/spec/praxis/extensions/support/spec_resources_sequel.rb +106 -0
- data/spec/praxis/mapper/selector_generator_spec.rb +306 -282
- data/spec/praxis/media_type_spec.rb +5 -139
- data/spec/praxis/middleware_app_spec.rb +1 -1
- data/spec/praxis/request_spec.rb +3 -22
- data/spec/praxis/request_stages/action_spec.rb +8 -1
- data/spec/praxis/resource_definition_spec.rb +1 -1
- data/spec/praxis/response_definition_spec.rb +15 -13
- data/spec/praxis/response_spec.rb +1 -1
- data/spec/praxis/route_spec.rb +2 -9
- data/spec/praxis/router_spec.rb +1 -1
- data/spec/praxis/routing_config_spec.rb +4 -13
- data/spec/praxis/types/multipart_array_spec.rb +4 -21
- data/spec/spec_app/app/controllers/instances.rb +1 -1
- data/spec/spec_app/config/environment.rb +0 -2
- data/spec/spec_app/design/api.rb +7 -1
- data/spec/spec_app/design/media_types/instance.rb +0 -8
- data/spec/spec_app/design/media_types/volume.rb +0 -12
- data/spec/spec_app/design/resources/instances.rb +1 -2
- data/spec/spec_helper.rb +17 -0
- data/spec/support/be_deep_equal_matcher.rb +39 -0
- data/spec/support/spec_media_types.rb +0 -73
- data/spec/support/spec_resources.rb +42 -49
- metadata +91 -56
- data/spec/praxis/handlers/xml_spec.rb +0 -177
- data/spec/praxis/links_spec.rb +0 -68
- data/spec/spec_app/app/models/person.rb +0 -3
data/lib/praxis/handlers/json.rb
CHANGED
@@ -17,6 +17,8 @@ module Praxis
|
|
17
17
|
# @param [String] document
|
18
18
|
# @return [Hash,Array] the structured-data representation of the document
|
19
19
|
def parse(document)
|
20
|
+
# Try to be nice and accept an empty string as an empty payload (seems nice to do for dumb http clients)
|
21
|
+
return nil if (document.nil? || document == '')
|
20
22
|
::JSON.parse(document)
|
21
23
|
end
|
22
24
|
|
@@ -1,3 +1,8 @@
|
|
1
|
+
# This is an example of a handler that can load and generate www-url-encoded payloads.
|
2
|
+
# Note that if you use your API to pass nil values for attributes as a way to unset their
|
3
|
+
# values, this handler will not work (as there isn't necessarily a defined "null" value in
|
4
|
+
# this encoding (although you can probably define how to encode/decode it and use it as such)
|
5
|
+
# Use at your own risk.
|
1
6
|
module Praxis
|
2
7
|
module Handlers
|
3
8
|
class WWWForm
|
@@ -1,3 +1,9 @@
|
|
1
|
+
# This is an example of a handler that can load and generate 'activesupport-style' xml payloads.
|
2
|
+
# Note that if you use your API to pass nil values for attributes as a way to unset their values,
|
3
|
+
# this handler will not work (as there isn't necessarily a defined "null" value in this encoding
|
4
|
+
# (although you can probably define how to encode/decode it and use it as such)
|
5
|
+
# Use at your own risk
|
6
|
+
|
1
7
|
module Praxis
|
2
8
|
module Handlers
|
3
9
|
class XML
|
data/lib/praxis/links.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'active_support/concern'
|
4
4
|
|
5
5
|
require 'praxis/extensions/field_selection/active_record_query_selector'
|
6
|
+
require 'praxis/extensions/attribute_filtering/active_record_filter_query_builder'
|
6
7
|
|
7
8
|
module Praxis
|
8
9
|
module Mapper
|
@@ -15,7 +16,7 @@ module Praxis
|
|
15
16
|
|
16
17
|
module ClassMethods
|
17
18
|
def _filter_query_builder_class
|
18
|
-
Praxis::Extensions::ActiveRecordFilterQueryBuilder
|
19
|
+
Praxis::Extensions::AttributeFiltering::ActiveRecordFilterQueryBuilder
|
19
20
|
end
|
20
21
|
|
21
22
|
def _field_selector_query_builder_class
|
@@ -46,18 +47,70 @@ module Praxis
|
|
46
47
|
else
|
47
48
|
raise "Unknown association type: #{v.class.name} on #{v.klass.name} for #{v.name}"
|
48
49
|
end
|
50
|
+
# Call out any local (i.e., of this model) columns that participate in the association
|
51
|
+
info[:local_key_columns] = local_columns_used_for_the_association(info[:type], v)
|
52
|
+
info[:remote_key_columns] = remote_columns_used_for_the_association(info[:type], v)
|
49
53
|
|
50
54
|
if v.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
51
55
|
info[:through] = v.through_reflection.name # TODO: is this correct?
|
52
56
|
end
|
53
|
-
|
54
|
-
# TODO: add more keys for the association to make true praxis mapper functions happy
|
55
57
|
hash[k.to_sym] = info
|
56
58
|
end
|
57
59
|
end
|
58
60
|
|
61
|
+
def _join_foreign_key_for(assoc_reflection)
|
62
|
+
maj, min, _ = ActiveRecord.gem_version.segments
|
63
|
+
if maj >= 6 && min >=1
|
64
|
+
assoc_reflection.join_foreign_key.to_sym
|
65
|
+
else
|
66
|
+
assoc_reflection.join_keys.foreign_key.to_sym
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def _join_primary_key_for(assoc_reflection)
|
71
|
+
maj, min, _ = ActiveRecord.gem_version.segments
|
72
|
+
if maj >= 6 && min >=1
|
73
|
+
assoc_reflection.join_primary_key.to_sym
|
74
|
+
else
|
75
|
+
assoc_reflection.join_keys.key.to_sym
|
76
|
+
end
|
77
|
+
end
|
78
|
+
private
|
79
|
+
def local_columns_used_for_the_association(type, assoc_reflection)
|
80
|
+
case type
|
81
|
+
when :one_to_many
|
82
|
+
# The associated table will point to us by key (usually the PK, but not always)
|
83
|
+
[_join_foreign_key_for(assoc_reflection)]
|
84
|
+
when :many_to_one
|
85
|
+
# We have the FKs to the associated model
|
86
|
+
[_join_foreign_key_for(assoc_reflection)]
|
87
|
+
when :many_to_many
|
88
|
+
ref = resolve_closest_through_reflection(assoc_reflection)
|
89
|
+
# The associated middle table will point to us by key (usually the PK, but not always)
|
90
|
+
[_join_foreign_key_for(ref)] # The foreign key that the last through table points to
|
91
|
+
else
|
92
|
+
raise "association type #{type} not supported"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def remote_columns_used_for_the_association(type, assoc_reflection)
|
97
|
+
# It seems that since the reflection is the target of the association, using the join_keys.key
|
98
|
+
# will always get us the right column
|
99
|
+
case type
|
100
|
+
when :one_to_many, :many_to_one, :many_to_many
|
101
|
+
[_join_primary_key_for(assoc_reflection)]
|
102
|
+
else
|
103
|
+
raise "association type #{type} not supported"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Keep following the association reflections as long as there are middle ones (i.e., through)
|
108
|
+
# until we come to the one next to the source
|
109
|
+
def resolve_closest_through_reflection(ref)
|
110
|
+
return ref unless ref.through_reflection?
|
111
|
+
resolve_closest_through_reflection( ref.through_reflection )
|
112
|
+
end
|
59
113
|
end
|
60
|
-
|
61
114
|
end
|
62
115
|
end
|
63
116
|
end
|
@@ -196,26 +196,33 @@ module Praxis::Mapper
|
|
196
196
|
end
|
197
197
|
|
198
198
|
# TODO: this shouldn't be needed if we incorporate it with the properties of the mapper...
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
199
|
+
# ...maybe what this means is that we can change it for a better DSL in the resource?
|
200
|
+
def self.filters_mapping(definition)
|
201
|
+
@_filters_map = \
|
202
|
+
case definition
|
203
|
+
when Hash
|
204
|
+
definition
|
205
|
+
when Array
|
206
|
+
definition.each_with_object({}) { |item, hash| hash[item.to_sym] = item }
|
207
|
+
else
|
208
|
+
raise "Resource.filters_mapping only allows a hash or an array"
|
209
|
+
end
|
205
210
|
end
|
206
211
|
|
207
212
|
def self.craft_filter_query(base_query, filters:) # rubocop:disable Metrics/AbcSize
|
208
|
-
if filters
|
209
|
-
|
213
|
+
if filters
|
214
|
+
raise "Must define the mapping of filters if want to use Filtering for resource: #{self}" unless @_filters_map
|
215
|
+
debug = Praxis::Application.instance.config.mapper.debug_queries
|
216
|
+
base_query = model._filter_query_builder_class.new(query: base_query, model: model, filters_map: @_filters_map, debug: debug).generate(filters)
|
210
217
|
end
|
211
218
|
|
212
219
|
base_query
|
213
220
|
end
|
214
221
|
|
215
|
-
def self.craft_field_selection_query(base_query, selectors
|
222
|
+
def self.craft_field_selection_query(base_query, selectors:) # rubocop:disable Metrics/AbcSize
|
216
223
|
if selectors && model._field_selector_query_builder_class
|
217
|
-
|
218
|
-
|
224
|
+
debug = Praxis::Application.instance.config.mapper.debug_queries
|
225
|
+
base_query = model._field_selector_query_builder_class.new(query: base_query, selectors: selectors, debug: debug).generate
|
219
226
|
end
|
220
227
|
|
221
228
|
base_query
|
@@ -1,101 +1,84 @@
|
|
1
1
|
module Praxis::Mapper
|
2
|
-
# Generates a set of selectors given a resource and
|
3
|
-
# list of resource attributes.
|
4
|
-
class SelectorGenerator
|
5
|
-
attr_reader :selectors
|
6
2
|
|
7
|
-
|
8
|
-
|
9
|
-
hash[key] = {select: Set.new, track: Set.new}
|
10
|
-
end
|
11
|
-
@seen = Hash.new do |hash, resource|
|
12
|
-
hash[resource] = Set.new
|
13
|
-
end
|
14
|
-
end
|
3
|
+
class SelectorGeneratorNode
|
4
|
+
attr_reader :select, :model, :resource, :tracks
|
15
5
|
|
16
|
-
def
|
17
|
-
|
18
|
-
@seen[resource] << fields
|
6
|
+
def initialize(resource)
|
7
|
+
@resource = resource
|
19
8
|
|
20
|
-
|
21
|
-
|
22
|
-
|
9
|
+
@select = Set.new
|
10
|
+
@select_star = false
|
11
|
+
@tracks = Hash.new
|
23
12
|
end
|
24
13
|
|
25
|
-
def
|
26
|
-
|
14
|
+
def add(fields)
|
15
|
+
fields.each do |name, field|
|
16
|
+
map_property(name, field)
|
17
|
+
end
|
18
|
+
self
|
27
19
|
end
|
28
20
|
|
29
|
-
def map_property(
|
21
|
+
def map_property(name, fields)
|
22
|
+
praxis_compat_model = resource.model && resource.model.respond_to?(:_praxis_associations)
|
30
23
|
if resource.properties.key?(name)
|
31
|
-
add_property(
|
32
|
-
elsif resource.model._praxis_associations.key?(name)
|
33
|
-
add_association(
|
24
|
+
add_property(name, fields)
|
25
|
+
elsif praxis_compat_model && resource.model._praxis_associations.key?(name)
|
26
|
+
add_association(name, fields)
|
34
27
|
else
|
35
|
-
add_select(
|
28
|
+
add_select(name)
|
36
29
|
end
|
37
30
|
end
|
38
31
|
|
39
|
-
def
|
40
|
-
|
41
|
-
return if selectors[resource.model][:select] == true
|
42
|
-
|
43
|
-
selectors[resource.model][:select] << name
|
44
|
-
end
|
45
|
-
|
46
|
-
def add_track(resource, name)
|
47
|
-
selectors[resource.model][:track] << name
|
48
|
-
end
|
49
|
-
|
50
|
-
def add_association(resource, name, fields)
|
32
|
+
def add_association(name, fields)
|
33
|
+
|
51
34
|
association = resource.model._praxis_associations.fetch(name) do
|
52
35
|
raise "missing association for #{resource} with name #{name}"
|
53
36
|
end
|
54
37
|
associated_resource = resource.model_map[association[:model]]
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
when :many_to_many
|
68
|
-
# If we haven't explicitly added the "through" option in the association
|
69
|
-
# then we'll assume the underlying ORM is able to fill in the gap. We will
|
70
|
-
# simply add the fields for the associated resource below
|
71
|
-
if association.key? :through
|
72
|
-
head, *tail = association[:through]
|
73
|
-
new_fields = tail.reverse.inject(fields) do |thing, step|
|
74
|
-
{step => thing}
|
75
|
-
end
|
76
|
-
return add_association(resource, head, new_fields)
|
77
|
-
else
|
78
|
-
add_track(resource, name)
|
38
|
+
unless associated_resource
|
39
|
+
raise "Whoops! could not find a resource associated with model #{association[:model]} (root resource #{resource})"
|
40
|
+
end
|
41
|
+
# Add the required columns in this model to make sure the association can be loaded
|
42
|
+
association[:local_key_columns].each {|col| add_select(col) }
|
43
|
+
|
44
|
+
node = SelectorGeneratorNode.new(associated_resource)
|
45
|
+
unless association[:remote_key_columns].empty?
|
46
|
+
# Make sure we add the required columns for this association to the remote model query
|
47
|
+
fields = {} if fields == true
|
48
|
+
new_fields_as_hash = association[:remote_key_columns].each_with_object({}) do|name, hash|
|
49
|
+
hash[name] = true
|
79
50
|
end
|
80
|
-
|
81
|
-
raise "no select applicable for #{association[:type].inspect}"
|
51
|
+
fields.merge!(new_fields_as_hash)
|
82
52
|
end
|
83
53
|
|
84
|
-
unless fields == true
|
85
|
-
|
86
|
-
|
87
|
-
|
54
|
+
node.add(fields) unless fields == true
|
55
|
+
|
56
|
+
self.merge_track(name, node)
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_select(name)
|
60
|
+
return @select_star = true if name == :*
|
61
|
+
return if @select_star
|
62
|
+
@select.add name
|
88
63
|
end
|
89
64
|
|
90
|
-
def add_property(
|
65
|
+
def add_property(name, fields)
|
91
66
|
dependencies = resource.properties[name][:dependencies]
|
67
|
+
# Always add the underlying association if we're overriding the name...
|
68
|
+
praxis_compat_model = resource.model && resource.model.respond_to?(:_praxis_associations)
|
69
|
+
if praxis_compat_model && resource.model._praxis_associations.key?(name)
|
70
|
+
add_association(name, fields)
|
71
|
+
end
|
92
72
|
if dependencies
|
93
73
|
dependencies.each do |dependency|
|
94
|
-
#
|
74
|
+
# To detect recursion, let's allow mapping depending fields to the same name of the property
|
75
|
+
# but properly detecting if it's a real association...in which case we've already added it above
|
95
76
|
if dependency == name
|
96
|
-
|
77
|
+
unless praxis_compat_model && resource.model._praxis_associations.key?(name)
|
78
|
+
add_select(name)
|
79
|
+
end
|
97
80
|
else
|
98
|
-
apply_dependency(
|
81
|
+
apply_dependency(dependency)
|
99
82
|
end
|
100
83
|
end
|
101
84
|
end
|
@@ -107,20 +90,61 @@ module Praxis::Mapper
|
|
107
90
|
{step => thing}
|
108
91
|
end
|
109
92
|
|
110
|
-
add_association(
|
93
|
+
add_association(head, new_fields)
|
111
94
|
end
|
112
95
|
|
113
|
-
def apply_dependency(
|
96
|
+
def apply_dependency(dependency)
|
114
97
|
case dependency
|
115
98
|
when Symbol
|
116
|
-
map_property(
|
99
|
+
map_property(dependency, true)
|
117
100
|
when String
|
118
101
|
head, tail = dependency.split('.').collect(&:to_sym)
|
119
102
|
raise "String dependencies can not be singular" if tail.nil?
|
120
103
|
|
121
|
-
add_association(
|
104
|
+
add_association(head, {tail => true})
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def merge_track( track_name, node )
|
109
|
+
raise "Cannot merge another node for association #{track_name}: incompatible model" unless node.model == self.model
|
110
|
+
|
111
|
+
existing = self.tracks[track_name]
|
112
|
+
if existing
|
113
|
+
node.select.each do|col_name|
|
114
|
+
existing.add_select(col_name)
|
115
|
+
end
|
116
|
+
node.tracks.each do |name, n|
|
117
|
+
existing.merge_track(name, n)
|
118
|
+
end
|
119
|
+
else
|
120
|
+
self.tracks[track_name] = node
|
122
121
|
end
|
123
122
|
end
|
124
123
|
|
124
|
+
def dump
|
125
|
+
hash = {}
|
126
|
+
hash[:model] = resource.model
|
127
|
+
if !@select.empty? || @select_star
|
128
|
+
hash[:columns] = @select_star ? [ :* ] : @select.to_a
|
129
|
+
end
|
130
|
+
unless @tracks.empty?
|
131
|
+
hash[:tracks] = @tracks.each_with_object({}) {|(name, node), hash| hash[name] = node.dump }
|
132
|
+
end
|
133
|
+
hash
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Generates a set of selectors given a resource and
|
138
|
+
# list of resource attributes.
|
139
|
+
class SelectorGenerator
|
140
|
+
# Entry point
|
141
|
+
def add(resource, fields)
|
142
|
+
@root = SelectorGeneratorNode.new(resource)
|
143
|
+
@root.add(fields)
|
144
|
+
end
|
145
|
+
|
146
|
+
def selectors
|
147
|
+
@root
|
148
|
+
end
|
125
149
|
end
|
126
150
|
end
|
@@ -1,23 +1,30 @@
|
|
1
1
|
require 'active_support/concern'
|
2
2
|
|
3
|
+
|
3
4
|
module Praxis::Mapper
|
4
5
|
module SequelCompat
|
5
6
|
extend ActiveSupport::Concern
|
6
7
|
|
7
|
-
|
8
|
+
included do
|
8
9
|
attr_accessor :_resource
|
9
10
|
end
|
10
11
|
|
11
12
|
module ClassMethods
|
12
13
|
def _filter_query_builder_class
|
14
|
+
# TODO: refactor the query builder, and add the explicit require in this file
|
13
15
|
Praxis::Extensions::SequelFilterQueryBuilder
|
14
16
|
end
|
15
17
|
|
18
|
+
def _field_selector_query_builder_class
|
19
|
+
Praxis::Extensions::FieldSelection::SequelQuerySelector
|
20
|
+
end
|
21
|
+
|
16
22
|
def _praxis_associations
|
17
23
|
orig = self.association_reflections.clone
|
18
|
-
|
19
24
|
orig.each do |k,v|
|
20
25
|
v[:model] = v.associated_class
|
26
|
+
v[:local_key_columns] = local_columns_used_for_the_association(v[:type], v)
|
27
|
+
v[:remote_key_columns] = remote_columns_used_for_the_association(v[:type], v)
|
21
28
|
if v.respond_to?(:primary_key)
|
22
29
|
v[:primary_key] = v.primary_key
|
23
30
|
else
|
@@ -31,7 +38,40 @@ module Praxis::Mapper
|
|
31
38
|
orig
|
32
39
|
end
|
33
40
|
|
41
|
+
private
|
42
|
+
def local_columns_used_for_the_association(type, assoc_reflection)
|
43
|
+
case type
|
44
|
+
when :one_to_many
|
45
|
+
# The associated table (or middle table if many to many) will point to us by PK
|
46
|
+
assoc_reflection[:primary_key_columns]
|
47
|
+
when :many_to_one
|
48
|
+
# We have the FKs to the associated model
|
49
|
+
assoc_reflection[:keys]
|
50
|
+
when :many_to_many
|
51
|
+
# The middle table if many to many) will point to us by key (usually the PK, but not always)
|
52
|
+
assoc_reflection[:left_primary_keys]
|
53
|
+
else
|
54
|
+
raise "association type #{type} not supported"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def remote_columns_used_for_the_association(type, assoc_reflection)
|
59
|
+
case type
|
60
|
+
when :one_to_many
|
61
|
+
# The columns in the associated table that will point back to the original association
|
62
|
+
assoc_reflection[:keys]
|
63
|
+
when :many_to_one
|
64
|
+
# The columns in the associated table that the children will point to (usually the PK, but not always) ??
|
65
|
+
[assoc_reflection.associated_class.primary_key]
|
66
|
+
when :many_to_many
|
67
|
+
# The middle table if many to many will point to us by key (usually the PK, but not always) ??
|
68
|
+
[assoc_reflection.associated_class.primary_key]
|
69
|
+
else
|
70
|
+
raise "association type #{type} not supported"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
34
74
|
end
|
35
75
|
|
36
76
|
end
|
37
|
-
end
|
77
|
+
end
|