ibrain-core 0.4.5 → 0.4.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/ibrain/base_controller.rb +12 -14
  3. data/app/controllers/ibrain/core/graphql_controller.rb +79 -83
  4. data/app/graphql/ibrain/base_schema.rb +50 -52
  5. data/app/graphql/ibrain/extentions/default_value.rb +7 -11
  6. data/app/graphql/ibrain/extentions/roles.rb +15 -20
  7. data/app/graphql/ibrain/extentions/session_required.rb +10 -14
  8. data/app/graphql/ibrain/lazy/base.rb +2 -6
  9. data/app/graphql/ibrain/loaders/association_loader.rb +51 -55
  10. data/app/graphql/ibrain/mutations/base_mutation.rb +52 -56
  11. data/app/graphql/ibrain/policies/base_policy.rb +43 -47
  12. data/app/graphql/ibrain/policies/graphql_policy.rb +2 -6
  13. data/app/graphql/ibrain/resolvers/base_aggregate.rb +6 -10
  14. data/app/graphql/ibrain/resolvers/base_resolver.rb +8 -12
  15. data/app/graphql/ibrain/types/aggregate_type.rb +4 -8
  16. data/app/graphql/ibrain/types/attribute_type.rb +2 -5
  17. data/app/graphql/ibrain/types/base_api_connection.rb +6 -10
  18. data/app/graphql/ibrain/types/base_api_edge.rb +4 -8
  19. data/app/graphql/ibrain/types/base_api_field.rb +7 -11
  20. data/app/graphql/ibrain/types/base_api_object.rb +6 -10
  21. data/app/graphql/ibrain/types/base_argument.rb +3 -7
  22. data/app/graphql/ibrain/types/base_connection.rb +7 -11
  23. data/app/graphql/ibrain/types/base_edge.rb +3 -7
  24. data/app/graphql/ibrain/types/base_enum.rb +1 -5
  25. data/app/graphql/ibrain/types/base_field.rb +6 -10
  26. data/app/graphql/ibrain/types/base_input_object.rb +6 -10
  27. data/app/graphql/ibrain/types/base_interface.rb +5 -9
  28. data/app/graphql/ibrain/types/base_object.rb +12 -16
  29. data/app/graphql/ibrain/types/base_scalar.rb +1 -5
  30. data/app/graphql/ibrain/types/base_type.rb +4 -10
  31. data/app/graphql/ibrain/types/base_union.rb +3 -7
  32. data/app/graphql/ibrain/types/filter_type.rb +1 -5
  33. data/app/graphql/ibrain/types/node_type.rb +4 -8
  34. data/app/graphql/ibrain/util/field_combiner.rb +5 -9
  35. data/app/graphql/ibrain/util/query_combiner.rb +4 -8
  36. data/app/models/concerns/ibrain/soft_deletable.rb +6 -8
  37. data/app/models/concerns/ibrain/user_api_authentication.rb +14 -16
  38. data/app/models/concerns/ibrain/user_methods.rb +11 -13
  39. data/app/models/ibrain/ability.rb +31 -32
  40. data/app/models/ibrain/aggregate.rb +5 -7
  41. data/app/models/ibrain/application_record.rb +2 -4
  42. data/app/models/ibrain/legacy_user.rb +7 -13
  43. data/app/models/ibrain/role.rb +6 -8
  44. data/app/models/ibrain/role_user.rb +8 -10
  45. data/app/repositories/ibrain/base_repository.rb +9 -11
  46. data/lib/generators/ibrain/core/model_generator.rb +17 -21
  47. data/lib/generators/ibrain/graphql/core.rb +55 -59
  48. data/lib/generators/ibrain/graphql/mutation_generator.rb +58 -66
  49. data/lib/generators/ibrain/graphql/object_generator.rb +58 -70
  50. data/lib/generators/ibrain/graphql/resolver_generator.rb +17 -25
  51. data/lib/generators/ibrain/graphql/resolvers_generator.rb +59 -67
  52. data/lib/generators/ibrain/graphql/templates/aggregate.erb +5 -7
  53. data/lib/generators/ibrain/graphql/templates/input.erb +5 -9
  54. data/lib/generators/ibrain/graphql/templates/mutation.erb +24 -26
  55. data/lib/generators/ibrain/graphql/templates/object.erb +6 -10
  56. data/lib/generators/ibrain/graphql/templates/resolver.erb +9 -11
  57. data/lib/generators/ibrain/graphql/templates/resolvers.erb +8 -10
  58. data/lib/generators/ibrain/graphql/type_generator.rb +84 -88
  59. data/lib/generators/ibrain/install/install_generator.rb +137 -140
  60. data/lib/generators/ibrain/install/templates/graphql/types/mutation_type.rb.tt +13 -15
  61. data/lib/generators/ibrain/install/templates/graphql/types/query_type.rb.tt +11 -13
  62. data/lib/generators/ibrain/install/templates/rubocop.yml.tt +83 -30
  63. data/lib/ibrain/app_configuration.rb +34 -36
  64. data/lib/ibrain/core/class_constantizer.rb +30 -33
  65. data/lib/ibrain/core/controller_helpers/auth.rb +53 -59
  66. data/lib/ibrain/core/controller_helpers/current_host.rb +5 -11
  67. data/lib/ibrain/core/controller_helpers/response.rb +43 -49
  68. data/lib/ibrain/core/controller_helpers/strong_parameters.rb +9 -15
  69. data/lib/ibrain/core/engine.rb +5 -9
  70. data/lib/ibrain/core/environment.rb +5 -9
  71. data/lib/ibrain/core/environment_extension.rb +13 -17
  72. data/lib/ibrain/core/role_configuration.rb +52 -54
  73. data/lib/ibrain/core/validators/email.rb +15 -17
  74. data/lib/ibrain/core/version.rb +2 -2
  75. data/lib/ibrain/core/versioned_value.rb +61 -65
  76. data/lib/ibrain/encryptor.rb +18 -20
  77. data/lib/ibrain/logger.rb +13 -15
  78. data/lib/ibrain/permission_sets/base.rb +22 -26
  79. data/lib/ibrain/permission_sets/super_user.rb +3 -7
  80. data/lib/ibrain/permitted_attributes.rb +18 -20
  81. data/lib/ibrain/preferences/configuration.rb +135 -137
  82. data/lib/ibrain/preferences/preferable.rb +158 -162
  83. data/lib/ibrain/preferences/preferable_class_methods.rb +112 -114
  84. data/lib/ibrain/user_class_handle.rb +22 -24
  85. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e0dfff268b752582b0733cd99f43f0784e20061a2898e29a0c779000e7a07cce
4
- data.tar.gz: b42ec5acdd8014ea314e3adacf6bbe6b470720d30aafa84d3b729efbc2d9f913
3
+ metadata.gz: bc329cadb1dbc83c7e0ea6e2a2e9f4eb6ef1ad3b74c8955e0f8ebb376ab329f2
4
+ data.tar.gz: 7371a38a6e0b9f531851e4b5ddf046a8e245fcd540211e1b3d51ce3a3dabb1be
5
5
  SHA512:
6
- metadata.gz: e618a908b94991ade401094a790c46815421f752b451ba481626075860723f059a8d9413b586f8ffe39fd9234b671ee264a500eb0b4de5fe9941a4f610ac6ba6
7
- data.tar.gz: 1e0ed241b8efc3c4f3545b88513d300a93702cbc7b65401764e562d9ec4322f84f2ae479b19247f212581cc43d6b40ab90163e7b5a3d5548f702c7a981fb63eb
6
+ metadata.gz: d69016d5aa23c6d5ea594298713db38c410087c56d3d7700302289ecd7e8bfa1d1db0eea2577cabf39036c37c9816f91a2e3f452cbb24161321d2f95f7af6be8
7
+ data.tar.gz: 3b1a1bf408ee1ce1ba4d53fcbd0d41042dddd45fd45854cd9cefa93c5b2e6e8ff6317e8153309fbf94a0d3ef929b7424187593a8535cc05e12d42dbdab224a13
@@ -1,20 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Ibrain
4
- class BaseController < Ibrain::Config.parent_controller.constantize
5
- include ActionController::Helpers
6
- include Ibrain::Core::ControllerHelpers::Response
7
- include Ibrain::Core::ControllerHelpers::StrongParameters
8
- include Ibrain::Core::ControllerHelpers::CurrentHost
9
- include Ibrain::Core::ControllerHelpers::Auth
3
+ class Ibrain::BaseController < Ibrain::Config.parent_controller.constantize
4
+ include ActionController::Helpers
5
+ include Ibrain::Core::ControllerHelpers::Response
6
+ include Ibrain::Core::ControllerHelpers::StrongParameters
7
+ include Ibrain::Core::ControllerHelpers::CurrentHost
8
+ include Ibrain::Core::ControllerHelpers::Auth
10
9
 
11
- include IbrainErrors
12
- include IbrainHandler
10
+ include IbrainErrors
11
+ include IbrainHandler
13
12
 
14
- protected
13
+ protected
15
14
 
16
- def cryptor
17
- Ibrain::Encryptor.new
18
- end
15
+ def cryptor
16
+ Ibrain::Encryptor.new
19
17
  end
20
- end
18
+ end
@@ -1,103 +1,99 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Ibrain
4
- module Core
5
- class GraphqlController < ::Ibrain::BaseController
6
- include Devise::Controllers::ScopedViews
7
-
8
- before_action :map_user_class_to_request
9
-
10
- helpers = %w(resource scope_name resource_name signed_in_resource
11
- resource_class resource_params devise_mapping)
12
- helper_method(*helpers)
13
-
14
- def execute
15
- query, variables, operation_name = normalize_entity
16
-
17
- result = schema.execute(
18
- query,
19
- variables: variables,
20
- context: {
21
- session: session,
22
- current_user: try_ibrain_current_user,
23
- controller: self,
24
- request: request
25
- },
26
- operation_name: operation_name,
27
- max_depth: max_depth(operation_name)
28
- )
29
-
30
- render_json_ok(result['data'], nil, result['errors'])
31
- end
32
-
33
- protected
3
+ class Ibrain::Core::GraphqlController < Ibrain::BaseController
4
+ include Devise::Controllers::ScopedViews
5
+
6
+ before_action :map_user_class_to_request
7
+
8
+ helpers = %w(resource scope_name resource_name signed_in_resource
9
+ resource_class resource_params devise_mapping)
10
+ helper_method(*helpers)
11
+
12
+ def execute
13
+ query, variables, operation_name = normalize_entity
14
+
15
+ result = schema.execute(
16
+ query,
17
+ variables: variables,
18
+ context: {
19
+ session: session,
20
+ current_user: try_ibrain_current_user,
21
+ controller: self,
22
+ request: request
23
+ },
24
+ operation_name: operation_name,
25
+ max_depth: max_depth(operation_name)
26
+ )
27
+
28
+ render_json_ok(result['data'], nil, result['errors'])
29
+ end
34
30
 
35
- def normalize_entity
36
- return [params[:query], prepare_variables(params[:variables]), params[:operationName]] if params[:variables].present?
37
- return [params[:query], prepare_variables(params[:variables]), 'IntrospectionQuery'] if params[:query].try(:include?, 'IntrospectionQuery')
31
+ protected
38
32
 
39
- operations = prepare_variables(params[:operations])
40
- query = operations['query'] || params[:query]
41
- variables = operations['variables'] || params[:variables]
42
- operation_name = operations['operationName'] || params[:operationName]
33
+ def normalize_entity
34
+ return [params[:query], prepare_variables(params[:variables]), params[:operationName]] if params[:variables].present?
35
+ return [params[:query], prepare_variables(params[:variables]), 'IntrospectionQuery'] if params[:query].try(:include?, 'IntrospectionQuery')
43
36
 
44
- if params[:map].present?
45
- JSON.parse(params[:map]).each do |k, arguments|
46
- argument_name = arguments.try(:first).try(:split, '.').try(:second)
47
- next if argument_name.blank?
37
+ operations = prepare_variables(params[:operations])
38
+ query = operations['query'] || params[:query]
39
+ variables = operations['variables'] || params[:variables]
40
+ operation_name = operations['operationName'] || params[:operationName]
48
41
 
49
- file = params[k]
42
+ if params[:map].present?
43
+ JSON.parse(params[:map]).each do |k, arguments|
44
+ argument_name = arguments.try(:first).try(:split, '.').try(:second)
45
+ next if argument_name.blank?
50
46
 
51
- if variables[argument_name].blank?
52
- variables[argument_name] = variables[argument_name].is_a?(Array) ? [file] : file
47
+ file = params[k]
53
48
 
54
- next
55
- end
49
+ if variables[argument_name].blank?
50
+ variables[argument_name] = variables[argument_name].is_a?(Array) ? [file] : file
56
51
 
57
- unless variables[argument_name].is_a?(Array)
58
- variables[argument_name] = [variables[argument_name]]
59
- end
52
+ next
53
+ end
60
54
 
61
- variables[argument_name] = variables[argument_name].concat([file]).compact
62
- end
55
+ unless variables[argument_name].is_a?(Array)
56
+ variables[argument_name] = [variables[argument_name]]
63
57
  end
64
58
 
65
- [query, variables, operation_name]
59
+ variables[argument_name] = variables[argument_name].concat([file]).compact
66
60
  end
61
+ end
67
62
 
68
- # Handle variables in form data, JSON body, or a blank value
69
- def prepare_variables(variables_param)
70
- case variables_param
71
- when String
72
- if variables_param.present?
73
- JSON.parse(variables_param) || {}
74
- else
75
- {}
76
- end
77
- when Hash
78
- variables_param
79
- when ActionController::Parameters
80
- variables_param.to_unsafe_hash # GraphQLRuby will validate name and type of incoming variables.
81
- when nil
82
- {}
83
- else
84
- raise ArgumentError, "Unexpected parameter: #{variables_param}"
85
- end
86
- end
63
+ [query, variables, operation_name]
64
+ end
87
65
 
88
- def schema
89
- Ibrain::Config.graphql_schema.safe_constantize
66
+ # Handle variables in form data, JSON body, or a blank value
67
+ def prepare_variables(variables_param)
68
+ case variables_param
69
+ when String
70
+ if variables_param.present?
71
+ JSON.parse(variables_param) || {}
72
+ else
73
+ {}
90
74
  end
75
+ when Hash
76
+ variables_param
77
+ when ActionController::Parameters
78
+ variables_param.to_unsafe_hash # GraphQLRuby will validate name and type of incoming variables.
79
+ when nil
80
+ {}
81
+ else
82
+ raise ArgumentError, "Unexpected parameter: #{variables_param}"
83
+ end
84
+ end
91
85
 
92
- def map_user_class_to_request
93
- return if request.env['devise.mapping'].present?
86
+ def schema
87
+ Ibrain::Config.graphql_schema.safe_constantize
88
+ end
94
89
 
95
- request.env['devise.mapping'] = Ibrain.user_class
96
- end
90
+ def map_user_class_to_request
91
+ return if request.env['devise.mapping'].present?
97
92
 
98
- def max_depth(operation_name)
99
- operation_name == 'IntrospectionQuery' ? nil : Ibrain::Config.graphql_max_depth
100
- end
101
- end
93
+ request.env['devise.mapping'] = Ibrain.user_class
94
+ end
95
+
96
+ def max_depth(operation_name)
97
+ operation_name == 'IntrospectionQuery' ? nil : Ibrain::Config.graphql_max_depth
102
98
  end
103
- end
99
+ end
@@ -1,55 +1,53 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Ibrain
4
- class BaseSchema < ::GraphQL::Schema
5
- use GraphQL::Batch
6
-
7
- max_depth Ibrain::Config.graphql_max_depth
8
- query_analyzer(Ibrain::LogQueryDepth)
9
-
10
- # use GraphQL::Guard.new(
11
- # policy_object: ::Ibrain::Config.graphql_policy.safe_constantize,
12
- # not_authorized: ->(type, field) { raise IbrainErrors::PermissionError.new("You not have permission to access #{type}.#{field}") }
13
- # )
14
-
15
- # Union and Interface Resolution
16
- def self.resolve_type(_abstract_type, _obj, _ctx)
17
- # TODO: Implement this function
18
- # to return the correct object type for `obj`
19
- raise(GraphQL::RequiredImplementationMissingError)
20
- end
21
-
22
- # Relay-style Object Identification:
23
-
24
- # Return a string UUID for `object`
25
- def self.id_from_object(object, type_definition, query_ctx)
26
- # Here's a simple implementation which:
27
- # - joins the type name & object.id
28
- # - encodes it with base64:
29
- # GraphQL::Schema::UniqueWithinType.encode(type_definition.name, object.id)
30
- end
31
-
32
- # Given a string UUID, find the object
33
- def self.object_from_id(id, query_ctx)
34
- # For example, to decode the UUIDs generated above:
35
- # type_name, item_id = GraphQL::Schema::UniqueWithinType.decode(id)
36
- #
37
- # Then, based on `type_name` and `id`
38
- # find an object in your application
39
- # ...
40
- end
41
-
42
- def self.field(*args, camelize: false, **kwargs, &block)
43
- # if camelize == false
44
- # # Also make a camelized field:
45
- # field(*args, camelize: false, **kwargs, &block)
46
- # end
47
- super
48
- end
49
-
50
- rescue_from(ActiveRecord::RecordNotFound) do |_err, _obj, _args, _ctx, field|
51
- # Raise a graphql-friendly error with a custom message
52
- raise GraphQL::ExecutionError, "#{field.type.unwrap.graphql_name} not found"
53
- end
3
+ class Ibrain::BaseSchema < GraphQL::Schema
4
+ use GraphQL::Batch
5
+
6
+ max_depth Ibrain::Config.graphql_max_depth
7
+ query_analyzer(Ibrain::LogQueryDepth)
8
+
9
+ # use GraphQL::Guard.new(
10
+ # policy_object: ::Ibrain::Config.graphql_policy.safe_constantize,
11
+ # not_authorized: ->(type, field) { raise IbrainErrors::PermissionError.new("You not have permission to access #{type}.#{field}") }
12
+ # )
13
+
14
+ # Union and Interface Resolution
15
+ def self.resolve_type(_abstract_type, _obj, _ctx)
16
+ # TODO: Implement this function
17
+ # to return the correct object type for `obj`
18
+ raise(GraphQL::RequiredImplementationMissingError)
54
19
  end
55
- end
20
+
21
+ # Relay-style Object Identification:
22
+
23
+ # Return a string UUID for `object`
24
+ def self.id_from_object(object, type_definition, query_ctx)
25
+ # Here's a simple implementation which:
26
+ # - joins the type name & object.id
27
+ # - encodes it with base64:
28
+ # GraphQL::Schema::UniqueWithinType.encode(type_definition.name, object.id)
29
+ end
30
+
31
+ # Given a string UUID, find the object
32
+ def self.object_from_id(id, query_ctx)
33
+ # For example, to decode the UUIDs generated above:
34
+ # type_name, item_id = GraphQL::Schema::UniqueWithinType.decode(id)
35
+ #
36
+ # Then, based on `type_name` and `id`
37
+ # find an object in your application
38
+ # ...
39
+ end
40
+
41
+ def self.field(*args, camelize: false, **kwargs, &block)
42
+ # if camelize == false
43
+ # # Also make a camelized field:
44
+ # field(*args, camelize: false, **kwargs, &block)
45
+ # end
46
+ super
47
+ end
48
+
49
+ rescue_from(ActiveRecord::RecordNotFound) do |_err, _obj, _args, _ctx, field|
50
+ # Raise a graphql-friendly error with a custom message
51
+ raise GraphQL::ExecutionError, "#{field.type.unwrap.graphql_name} not found"
52
+ end
53
+ end
@@ -1,15 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Ibrain
4
- module Extentions
5
- class DefaultValue < GraphQL::Schema::FieldExtension
6
- def after_resolve(value:, **_rest)
7
- if value.nil?
8
- options[:default_value]
9
- else
10
- value
11
- end
12
- end
3
+ class Ibrain::Extentions::DefaultValue < GraphQL::Schema::FieldExtension
4
+ def after_resolve(value:, **_rest)
5
+ if value.nil?
6
+ options[:default_value]
7
+ else
8
+ value
13
9
  end
14
10
  end
15
- end
11
+ end
@@ -1,27 +1,22 @@
1
1
  # frozen_string_literal: true
2
+ class Ibrain::Extentions::Roles < GraphQL::Schema::FieldExtension
3
+ def after_resolve(object:, value:, **_rest)
4
+ raise IbrainErrors::PermissionError.new("You not have permission to access #{field&.name}") if is_invalid_role(object)
2
5
 
3
- module Ibrain
4
- module Extentions
5
- class Roles < GraphQL::Schema::FieldExtension
6
- def after_resolve(object:, value:, **_rest)
7
- raise IbrainErrors::PermissionError.new("You not have permission to access #{field&.name}") if is_invalid_role(object)
8
-
9
- value
10
- end
6
+ value
7
+ end
11
8
 
12
- private
9
+ private
13
10
 
14
- def is_invalid_role(object)
15
- roles = options.try(:fetch, :roles, [])
16
- current_user = object.try(:context).try(:fetch, :current_user, nil)
17
- role = current_user.try(:role) || current_user.try(:graphql_role)
11
+ def is_invalid_role(object)
12
+ roles = options.try(:fetch, :roles, [])
13
+ current_user = object.try(:context).try(:fetch, :current_user, nil)
14
+ role = current_user.try(:role) || current_user.try(:graphql_role)
18
15
 
19
- return if roles.blank?
20
- return true if current_user.blank?
21
- return false if roles.include?(role)
16
+ return if roles.blank?
17
+ return true if current_user.blank?
18
+ return false if roles.include?(role)
22
19
 
23
- true
24
- end
25
- end
20
+ true
26
21
  end
27
- end
22
+ end
@@ -1,20 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Ibrain
4
- module Extentions
5
- class SessionRequired < GraphQL::Schema::FieldExtension
6
- def resolve(object:, arguments:, **rest)
7
- raise ActionController::InvalidAuthenticityToken, I18n.t('ibrain.errors.session.invalid_session') if is_invalid_session(object)
3
+ class Ibrain::Extentions::SessionRequired < GraphQL::Schema::FieldExtension
4
+ def resolve(object:, arguments:, **rest)
5
+ raise ActionController::InvalidAuthenticityToken, I18n.t('ibrain.errors.session.invalid_session') if is_invalid_session(object)
8
6
 
9
- # yield the current time as `memo`
10
- yield(object, arguments, rest)
11
- end
7
+ # yield the current time as `memo`
8
+ yield(object, arguments, rest)
9
+ end
12
10
 
13
- private
11
+ private
14
12
 
15
- def is_invalid_session(object)
16
- object.try(:context).try(:fetch, :current_user, nil).blank? && options.try(:fetch, :session_required, false)
17
- end
18
- end
13
+ def is_invalid_session(object)
14
+ object.try(:context).try(:fetch, :current_user, nil).blank? && options.try(:fetch, :session_required, false)
19
15
  end
20
- end
16
+ end
@@ -1,8 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Ibrain
4
- module Lazy
5
- class Base < GraphQL::Schema::Resolver
6
- end
7
- end
8
- end
3
+ class Ibrain::Lazy::Base < GraphQL::Schema::Resolver
4
+ end
@@ -2,60 +2,56 @@
2
2
 
3
3
  # ref: https://zenn.dev/necocoa/articles/setup-graphql-batch
4
4
 
5
- module Ibrain
6
- module Loaders
7
- class AssociationLoader < GraphQL::Batch::Loader
8
- def self.validate(model, association_name)
9
- new(model, association_name)
10
- nil
11
- end
12
-
13
- def initialize(model, association_name)
14
- super()
15
- @model = model
16
- @association_name = association_name
17
- validate
18
- end
19
-
20
- def load(record)
21
- unless record.is_a?(@model)
22
- raise TypeError,
23
- "#{@model} loader can't load association for #{record.class}"
24
- end
25
- return Promise.resolve(read_association(record)) if association_loaded?(record)
26
-
27
- super
28
- end
29
-
30
- # We want to load the associations on all records, even if they have the same id
31
- def cache_key(record)
32
- record.object_id
33
- end
34
-
35
- def perform(records)
36
- preload_association(records)
37
- records.each { |record| fulfill(record, read_association(record)) }
38
- end
39
-
40
- private
41
-
42
- def validate
43
- return if @model.reflect_on_association(@association_name)
44
-
45
- raise ArgumentError, "No association #{@association_name} on #{@model}"
46
- end
47
-
48
- def preload_association(records)
49
- ::ActiveRecord::Associations::Preloader.new.preload(records, @association_name)
50
- end
51
-
52
- def read_association(record)
53
- record.public_send(@association_name)
54
- end
55
-
56
- def association_loaded?(record)
57
- record.association(@association_name).loaded?
58
- end
5
+ class Ibrain::Loaders::AssociationLoader < GraphQL::Batch::Loader
6
+ def self.validate(model, association_name)
7
+ new(model, association_name)
8
+ nil
9
+ end
10
+
11
+ def initialize(model, association_name)
12
+ super()
13
+ @model = model
14
+ @association_name = association_name
15
+ validate
16
+ end
17
+
18
+ def load(record)
19
+ unless record.is_a?(@model)
20
+ raise TypeError,
21
+ "#{@model} loader can't load association for #{record.class}"
59
22
  end
23
+ return Promise.resolve(read_association(record)) if association_loaded?(record)
24
+
25
+ super
26
+ end
27
+
28
+ # We want to load the associations on all records, even if they have the same id
29
+ def cache_key(record)
30
+ record.object_id
31
+ end
32
+
33
+ def perform(records)
34
+ preload_association(records)
35
+ records.each { |record| fulfill(record, read_association(record)) }
36
+ end
37
+
38
+ private
39
+
40
+ def validate
41
+ return if @model.reflect_on_association(@association_name)
42
+
43
+ raise ArgumentError, "No association #{@association_name} on #{@model}"
44
+ end
45
+
46
+ def preload_association(records)
47
+ ::ActiveRecord::Associations::Preloader.new.preload(records, @association_name)
48
+ end
49
+
50
+ def read_association(record)
51
+ record.public_send(@association_name)
52
+ end
53
+
54
+ def association_loaded?(record)
55
+ record.association(@association_name).loaded?
60
56
  end
61
- end
57
+ end