Module: DataloaderRelationProxy
- Defined in:
- lib/dataloader_relation_proxy.rb,
lib/dataloader_relation_proxy/lazy.rb,
lib/dataloader_relation_proxy/record.rb,
lib/dataloader_relation_proxy/version.rb,
lib/dataloader_relation_proxy/collection.rb,
lib/dataloader_relation_proxy/active_record_object.rb
Overview
Top level namespace for a system that proxies activerecord relationships through GraphQL dataloaders.
Defined Under Namespace
Modules: Lazy Classes: ActiveRecordObject, Collection, Record
Constant Summary collapse
- Proxies =
Namespace to house the proxy classes so they are easily addressable
Module.new
- Error =
Class.new(StandardError)
- DELEGATE_TO_MODEL_METHODS =
%i[ === == eql? equal? <=> < > <= >= ].freeze
- VERSION =
'0.1.0'
Class Method Summary collapse
-
.activerecord_belongs_to(klass, reflection) ⇒ Object
Define a getter that works something like an ActiveRecord belongs_to relationship, except using a dataloader.
-
.activerecord_has_many(klass, reflection) ⇒ Object
Define a getter that works something like an ActiveRecord has_many relationship, except using a dataloader.
-
.catch_remaining_methods!(klass) ⇒ Object
rubocop:disable Metrics/MethodLength.
-
.define_belongs_to_accessors!(klass, model) ⇒ Object
Given an activerecord model and wrapper class, define an accessor on the wrapper class for each belongs_to relationship on the model driven by GraphQL::Datalaoder.
-
.define_for!(model) ⇒ Object
Defines a wrapper class for the provided model.
-
.define_has_many_accessors!(klass, model) ⇒ Object
Given an activerecord model and wrapper class, define an accessor on the wrapper class for each has_many relationship on the model driven by GraphQL::Datalaoder.
-
.defined_for?(model) ⇒ Boolean
Determine if there is already a wrapper class defined for the given model.
-
.delegate_class_methods!(klass, model) ⇒ Object
Delegates some class methods to the underlying model class so that instances of this proxy can more readily stand in for a model instance.
-
.for(model) ⇒ Object
Ensure a wrapper class is defined for the given model and return it.
Class Method Details
.activerecord_belongs_to(klass, reflection) ⇒ Object
Define a getter that works something like an ActiveRecord belongs_to relationship, except using a dataloader.
148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/dataloader_relation_proxy.rb', line 148 def self.activerecord_belongs_to(klass, reflection) klass.define_method(reflection.name) do instance = @dataloader.with( ActiveRecordObject, reflection.klass, reflection.association_primary_key ).load(@object.send(reflection.foreign_key)) return nil if instance.nil? DataloaderRelationProxy.for(instance.class).new(instance, @dataloader) end end |
.activerecord_has_many(klass, reflection) ⇒ Object
Define a getter that works something like an ActiveRecord has_many relationship, except using a dataloader.
163 164 165 166 167 |
# File 'lib/dataloader_relation_proxy.rb', line 163 def self.activerecord_has_many(klass, reflection) klass.define_method(reflection.name) do Collection.new(@object.send(reflection.name), @dataloader) end end |
.catch_remaining_methods!(klass) ⇒ Object
rubocop:disable Metrics/MethodLength
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/dataloader_relation_proxy.rb', line 127 def self.catch_remaining_methods!(klass) klass.instance_eval do define_method(:method_missing) do |method_name, *args, &block| raise NoMethodError, "Missing method '#{method_name}' on #{@object}" unless @object.respond_to?(method_name) self.class.instance_eval do delegate method_name, to: :@object end @object.send(method_name, *args, &block) end define_method(:respond_to_missing?) do |method_name, include_private = false| @object.respond_to?(method_name) || super(method_name, include_private) end end end |
.define_belongs_to_accessors!(klass, model) ⇒ Object
Given an activerecord model and wrapper class, define an accessor on the wrapper class for each belongs_to relationship on the model driven by GraphQL::Datalaoder.
78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/dataloader_relation_proxy.rb', line 78 def self.define_belongs_to_accessors!(klass, model) return unless model.respond_to?(:reflect_on_all_associations) model.reflect_on_all_associations(:belongs_to).each do |reflection| # not sure how to handle this yet next if reflection.polymorphic? self.for(reflection.klass) klass.instance_eval do DataloaderRelationProxy.activerecord_belongs_to(klass, reflection) end end end |
.define_for!(model) ⇒ Object
Defines a wrapper class for the provided model. This wrapper creates classes that extend DataloaderRelationProxy::Record and then defines relationship accessors that are efficiently batched by GraphQL::Dataloader.
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/dataloader_relation_proxy.rb', line 39 def self.define_for!(model) @defined << model # Recursively define the namespace and wrapper class klass = model.name.split('::').reduce(Proxies) do |memo, value| next memo.const_get(value, false) if memo.const_defined?(value, false) memo.const_set(value, Class.new(DataloaderRelationProxy::Record)) end define_belongs_to_accessors!(klass, model) define_has_many_accessors!(klass, model) delegate_class_methods!(klass, model) catch_remaining_methods!(klass) end |
.define_has_many_accessors!(klass, model) ⇒ Object
Given an activerecord model and wrapper class, define an accessor on the wrapper class for each has_many relationship on the model driven by GraphQL::Datalaoder.
100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/dataloader_relation_proxy.rb', line 100 def self.define_has_many_accessors!(klass, model) return unless model.respond_to?(:reflect_on_all_associations) model.reflect_on_all_associations(:has_many).each do |reflection| # not sure how to handle this yet next if reflection.polymorphic? klass.instance_eval do DataloaderRelationProxy.activerecord_has_many(klass, reflection) end end end |
.defined_for?(model) ⇒ Boolean
Determine if there is already a wrapper class defined for the given model.
58 59 60 |
# File 'lib/dataloader_relation_proxy.rb', line 58 def self.defined_for?(model) @defined.include?(model) end |
.delegate_class_methods!(klass, model) ⇒ Object
Delegates some class methods to the underlying model class so that instances of this proxy can more readily stand in for a model instance.
118 119 120 121 122 123 124 |
# File 'lib/dataloader_relation_proxy.rb', line 118 def self.delegate_class_methods!(klass, model) DELEGATE_TO_MODEL_METHODS.each do |m| klass.define_singleton_method(m) do |other| model.send(m, other) end end end |
.for(model) ⇒ Object
Ensure a wrapper class is defined for the given model and return it.
65 66 67 68 69 |
# File 'lib/dataloader_relation_proxy.rb', line 65 def self.for(model) define_for!(model) unless defined_for?(model) DataloaderRelationProxy::Proxies.const_get(model.name, false) end |