ibrain-core 0.4.5 → 0.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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