Module: Rails::GraphQL::Helpers::LeafFromAr
- Defined in:
- lib/rails/graphql/helpers/leaf_from_ar.rb
Overview
Helper module allowing leaf values to be collected direct from ActiveRecord. It also helps AR Adapters to define the necessary methods and settings to operate with this extractor.
TODO: Implement ActiveRecord serialization
Class Method Summary collapse
Instance Method Summary collapse
-
#ar_type ⇒ Object
Identifies the ActiveRecord type (actually it uses the ActiveModel::Type#type method, but ActiveRecord uses the same reference) of this object.
-
#define_for(adapter, **settings) ⇒ Object
Helper method that should be used for ActiveRecord adapters in order to provide the correct methods and settings for retrieving the given value direct from a query.
-
#from_ar(ar_object, attribute) ⇒ Object
Returns an Arel object that represents how this object is serialized direct from the query.
-
#from_ar?(ar_object, attribute) ⇒ Boolean
If a class extend this module, we assume that it can serialize attributes direct from the query point of view.
Class Method Details
.extended(other) ⇒ Object
12 13 14 15 16 17 18 19 20 |
# File 'lib/rails/graphql/helpers/leaf_from_ar.rb', line 12 def self.extended(other) # Defines which type exactly represents the scalar type on the # ActiveRecord adapter for casting purposes other.class_attribute :ar_adapter_type, instance_accessor: false, default: {} # A list of ActiveRecord aliases per adapter to skip casting other.class_attribute :ar_adapter_aliases, instance_accessor: false, default: (Hash.new { |h, k| h[k] = Set.new }) end |
Instance Method Details
#ar_type ⇒ Object
Identifies the ActiveRecord type (actually it uses the ActiveModel::Type#type method, but ActiveRecord uses the same reference) of this object. When mismatching, the query must cast the value.
26 27 28 |
# File 'lib/rails/graphql/helpers/leaf_from_ar.rb', line 26 def ar_type :string end |
#define_for(adapter, **settings) ⇒ Object
Helper method that should be used for ActiveRecord adapters in order to provide the correct methods and settings for retrieving the given value direct from a query.
Options
-
:fetch
- A specific function to build the arel for fetching the attribute -
:cast
- A function to cast an attribute to the correct value -
:type
- A symbol that represents the exactly database type that matches the ActiveRecord type (ie. :varchar for :string) -
:aliases
- An array of AR aliases that for the adapter they are equivalent
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/rails/graphql/helpers/leaf_from_ar.rb', line 88 def define_for(adapter, **settings) adapter = ar_adapters[adapter] if ar_adapters.key?(adapter) raise ArgumentError, (+<<~MSG).squish unless ar_adapters.values.include?(adapter) The given #{adapter.inspect} adapter is not a valid option. The valid options are: #{ar_adapters.to_a.flatten.map(&:inspect).to_sentence}. MSG define_singleton_method("from_#{adapter}_adapter", &settings[:fetch]) \ if settings.key?(:fetch) define_singleton_method("cast_#{adapter}_attribute", &settings[:cast]) \ if settings.key?(:cast) ar_adapter_type[adapter] = settings[:type] if settings.key?(:type) ar_adapter_aliases[adapter] += ::Array.wrap(settings[:aliases]) \ if settings.key?(:aliases) end |
#from_ar(ar_object, attribute) ⇒ Object
Returns an Arel object that represents how this object is serialized direct from the query
This happens in 3 parts
-
It finds a method to get the arel representation of the accessor of the given attribute
-
If the attribute type mismatch the ar type or any of its aliases, then invoke a adapter-specific cast
-
If necessary, adapters can describe a specific way to serialize the arel attribute to ensure equivalency
Example
Lets imagine a scenario where the adapter is the PostgreSQL
, the attribute is a data
field with enum
type from a sample
table and the result must be a binary base64 data:
-
Sample.arel_attribute(:data) # => “samples”.“data”
-
Arel::Nodes::NamedFunction.new(‘CAST’, [arel_object, Arel.sql(‘text’)]) # => CAST(“samples”.“data” AS text)
-
Arel::Nodes::NamedFunction.new(‘ENCODE’, [arel_object, Arel.sql(“‘base64”)]) # => ENCODE(CAST(“samples”.“data” AS text), ’base64’)
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/rails/graphql/helpers/leaf_from_ar.rb', line 60 def from_ar(ar_object, attribute) key = adapter_key(ar_object) method_name = "from_#{key}_adapter" method_name = 'from_abstract_adapter' unless respond_to?(method_name, true) arel_object = send(method_name, ar_object, attribute) return if arel_object.nil? arel_object = try("cast_#{key}_attribute", arel_object, ar_adapter_type[key]) \ unless match_ar_type?(ar_object, attribute, key) return if arel_object.nil? method_name = "#{key}_serialize" respond_to?(method_name, true) ? try(method_name, arel_object) : arel_object end |
#from_ar?(ar_object, attribute) ⇒ Boolean
If a class extend this module, we assume that it can serialize attributes direct from the query point of view
32 33 34 |
# File 'lib/rails/graphql/helpers/leaf_from_ar.rb', line 32 def from_ar?(ar_object, attribute) ar_object&.has_attribute?(attribute) end |