Class: ElasticGraph::SchemaDefinition::Factory
- Inherits:
-
Object
- Object
- ElasticGraph::SchemaDefinition::Factory
- Defined in:
- lib/elastic_graph/schema_definition/factory.rb
Overview
A class responsible for instantiating all schema elements. We want all schema element instantiation to go through this one class to support extension libraries. ElasticGraph supports extension libraries that provide modules that get extended onto specific instances of ElasticGraph framework classes. We prefer this approach rather than having extension library modules applied via ‘include` or `prepend`, because they _permanently modify_ the host classes. ElasticGraph is designed to avoid all mutable global state, and that includes mutations to ElasticGraph class ancestor chains from extension libraries.
Concretely, if we included or prepended extension libraries modules, we’d have a hard time keeping our tests order-independent and deterministic while running all the ElasticGraph test suites in the same Ruby process. A test using an extension library could cause a core ElasticGraph class to get mutated in a way that impacts a test that runs in the same process later. Instead, we expect extension libraries to hook into ElasticGraph using ‘extend` on particular object instances.
But that creates a bit of a problem: how can an extension library extend a module onto every instance of a specific type of schema element while it is in use? The answer is this factory class:
- An extension library can extend a module onto `schema.factory`.
- That module can in turn override any of these factory methods and extend another module onto the schema
element instances.
Constant Summary collapse
- @@deprecated_element_new =
prevent_non_factory_instantiation_of(SchemaElements::DeprecatedElement)
- @@argument_new =
prevent_non_factory_instantiation_of(SchemaElements::Argument)
- @@built_in_types_new =
prevent_non_factory_instantiation_of(SchemaElements::BuiltInTypes)
- @@directive_new =
prevent_non_factory_instantiation_of(SchemaElements::Directive)
- @@enum_type_new =
prevent_non_factory_instantiation_of(SchemaElements::EnumType)
- @@enum_value_new =
prevent_non_factory_instantiation_of(SchemaElements::EnumValue)
- @@enums_for_indexed_types_new =
prevent_non_factory_instantiation_of(SchemaElements::EnumsForIndexedTypes)
- @@field_new =
prevent_non_factory_instantiation_of(SchemaElements::Field)
- @@graphql_sdl_enumerator_new =
prevent_non_factory_instantiation_of(SchemaElements::GraphQLSDLEnumerator)
- @@input_field_new =
prevent_non_factory_instantiation_of(SchemaElements::InputField)
- @@input_type_new =
prevent_non_factory_instantiation_of(SchemaElements::InputType)
- @@interface_type_new =
prevent_non_factory_instantiation_of(SchemaElements::InterfaceType)
- @@object_type_new =
prevent_non_factory_instantiation_of(SchemaElements::ObjectType)
- @@scalar_type_new =
prevent_non_factory_instantiation_of(SchemaElements::ScalarType)
- @@sort_order_enum_value_new =
prevent_non_factory_instantiation_of(SchemaElements::SortOrderEnumValue)
- @@type_reference_new =
prevent_non_factory_instantiation_of(SchemaElements::TypeReference)
- @@type_with_subfields_new =
prevent_non_factory_instantiation_of(SchemaElements::TypeWithSubfields)
- @@union_type_new =
prevent_non_factory_instantiation_of(SchemaElements::UnionType)
- @@field_source_new =
prevent_non_factory_instantiation_of(SchemaElements::FieldSource)
- @@relationship_new =
prevent_non_factory_instantiation_of(SchemaElements::Relationship)
Class Method Summary collapse
-
.prevent_non_factory_instantiation_of(klass) ⇒ Object
Helper method to help enforce our desired invariant: we want every instantiation of these schema element classes to happen via this factory method provided here.
Instance Method Summary collapse
- #build_relay_pagination_types(type_name, include_total_edge_count: false, derived_indexed_types: [], support_pagination: true, &customize_connection) ⇒ Object
-
#build_standard_filter_input_types_for_index_leaf_type(source_type, name_prefix: source_type, &define_filter_fields) ⇒ Object
Builds the standard set of filter input types for types which are indexing leaf types.
-
#build_standard_filter_input_types_for_index_object_type(source_type, name_prefix: source_type, &define_filter_fields) ⇒ Object
Builds the standard set of filter input types for types which are indexing object types.
-
#initialize(state) ⇒ Factory
constructor
A new instance of Factory.
-
#new_aggregated_values_type_for_index_leaf_type(index_leaf_type) ⇒ Object
Responsible for creating a new ‘*AggregatedValues` type for an index leaf type.
- #new_argument(field, name, value_type) ⇒ Object
- #new_built_in_types(api) ⇒ Object
- #new_deprecated_element(name, defined_at:, defined_via:) ⇒ Object
- #new_directive(name, arguments) ⇒ Object
- #new_enum_type(name, &block) ⇒ Object
- #new_enum_value(name, original_name) ⇒ Object
- #new_enums_for_indexed_types ⇒ Object
- #new_field_source(relationship_name:, field_path:) ⇒ Object
- #new_filter_input_type(source_type, name_prefix: source_type, category: :filter_input) ⇒ Object
- #new_graphql_sdl_enumerator(all_types_except_root_query_type) ⇒ Object
- #new_input_type(name) ⇒ Object
- #new_interface_type(name) ⇒ Object
- #new_object_type(name) ⇒ Object
- #new_relationship(field, cardinality:, related_type:, foreign_key:, direction:) ⇒ Object
- #new_scalar_type(name) ⇒ Object
- #new_sort_order_enum_value(enum_value, sort_order_field_path) ⇒ Object
- #new_type_reference(name) ⇒ Object
- #new_type_with_subfields(schema_kind, name, wrapping_type:, field_factory:) ⇒ Object
- #new_union_type(name) ⇒ Object
Constructor Details
#initialize(state) ⇒ Factory
Returns a new instance of Factory.
58 59 60 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 58 def initialize(state) @state = state end |
Class Method Details
.prevent_non_factory_instantiation_of(klass) ⇒ Object
Helper method to help enforce our desired invariant: we want every instantiation of these schema element classes to happen via this factory method provided here. To enforce that, this helper returns the ‘new` method (as a `Method` object) after removing it from the given class. That makes it impossible for `new` to be called by anyone except from the factory using the captured method object.
66 67 68 69 70 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 66 def self.prevent_non_factory_instantiation_of(klass) klass.method(:new).tap do klass.singleton_class.undef_method :new end end |
Instance Method Details
#build_relay_pagination_types(type_name, include_total_edge_count: false, derived_indexed_types: [], support_pagination: true, &customize_connection) ⇒ Object
196 197 198 199 200 201 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 196 def build_relay_pagination_types(type_name, include_total_edge_count: false, derived_indexed_types: [], support_pagination: true, &customize_connection) [ (edge_type_for(type_name) if support_pagination), connection_type_for(type_name, include_total_edge_count, derived_indexed_types, support_pagination, &customize_connection) ].compact end |
#build_standard_filter_input_types_for_index_leaf_type(source_type, name_prefix: source_type, &define_filter_fields) ⇒ Object
Builds the standard set of filter input types for types which are indexing leaf types.
All GraphQL leaf types (enums and scalars) are indexing leaf types, but some GraphQL object types are as well. For example, ‘GeoLocation` is an object type in GraphQL (with separate lat/long fields) but is an indexing leaf type because we use the datastore `geo_point` type for it.
175 176 177 178 179 180 181 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 175 def build_standard_filter_input_types_for_index_leaf_type(source_type, name_prefix: source_type, &define_filter_fields) single_value_filter = new_filter_input_type(source_type, name_prefix: name_prefix, &define_filter_fields) list_filter = new_list_filter_input_type(source_type, name_prefix: name_prefix, any_satisfy_type_category: :list_element_filter_input) list_element_filter = new_list_element_filter_input_type(source_type, name_prefix: name_prefix, &define_filter_fields) [single_value_filter, list_filter, list_element_filter] end |
#build_standard_filter_input_types_for_index_object_type(source_type, name_prefix: source_type, &define_filter_fields) ⇒ Object
Builds the standard set of filter input types for types which are indexing object types.
Most GraphQL object types are indexing object types as well, but not all. For example, ‘GeoLocation` is an object type in GraphQL (with separate lat/long fields) but is an indexing leaf type because we use the datastore `geo_point` type for it.
188 189 190 191 192 193 194 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 188 def build_standard_filter_input_types_for_index_object_type(source_type, name_prefix: source_type, &define_filter_fields) single_value_filter = new_filter_input_type(source_type, name_prefix: name_prefix, &define_filter_fields) list_filter = new_list_filter_input_type(source_type, name_prefix: name_prefix, any_satisfy_type_category: :filter_input) fields_list_filter = new_fields_list_filter_input_type(source_type, name_prefix: name_prefix) [single_value_filter, list_filter, fields_list_filter] end |
#new_aggregated_values_type_for_index_leaf_type(index_leaf_type) ⇒ Object
Responsible for creating a new ‘*AggregatedValues` type for an index leaf type.
An index leaf type is a scalar, enum, object type that is backed by a single, indivisible field in the index. All scalar and enum types are index leaf types, and object types rarely (but sometimes) are. For example, the ‘GeoLocation` object type has two subfields (`latitude` and `longitude`) but is backed by a single `geo_point` field in the index, so it is an index leaf type.
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 271 def new_aggregated_values_type_for_index_leaf_type(index_leaf_type) new_object_type @state.type_ref(index_leaf_type).as_aggregated_values.name do |type| type.graphql_only true type.documentation "A return type used from aggregations to provided aggregated values over `#{index_leaf_type}` fields." type. = {elasticgraph_category: :scalar_aggregated_values} type.field @state.schema_elements.approximate_distinct_value_count, "JsonSafeLong", graphql_only: true do |f| # Note: the 1-6% accuracy figure comes from the Elasticsearch docs: # https://www.elastic.co/guide/en/elasticsearch/reference/8.10/search-aggregations-metrics-cardinality-aggregation.html#_counts_are_approximate f.documentation <<~EOS An approximation of the number of unique values for this field within this grouping. The approximation uses the HyperLogLog++ algorithm from the [HyperLogLog in Practice](https://research.google.com/pubs/archive/40671.pdf) paper. The accuracy of the returned value varies based on the specific dataset, but it usually differs from the true distinct value count by less than 7%. EOS f. empty_bucket_value: 0, function: :cardinality end yield type end end |
#new_argument(field, name, value_type) ⇒ Object
77 78 79 80 81 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 77 def new_argument(field, name, value_type) @@argument_new.call(@state, field, name, value_type).tap do |argument| yield argument if block_given? end end |
#new_built_in_types(api) ⇒ Object
84 85 86 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 84 def new_built_in_types(api) @@built_in_types_new.call(api, @state) end |
#new_deprecated_element(name, defined_at:, defined_via:) ⇒ Object
72 73 74 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 72 def new_deprecated_element(name, defined_at:, defined_via:) @@deprecated_element_new.call(schema_def_state: @state, name: name, defined_at: defined_at, defined_via: defined_via) end |
#new_directive(name, arguments) ⇒ Object
89 90 91 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 89 def new_directive(name, arguments) @@directive_new.call(name, arguments) end |
#new_enum_type(name, &block) ⇒ Object
94 95 96 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 94 def new_enum_type(name, &block) @@enum_type_new.call(@state, name, &(_ = block)) end |
#new_enum_value(name, original_name) ⇒ Object
99 100 101 102 103 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 99 def new_enum_value(name, original_name) @@enum_value_new.call(@state, name, original_name) do |enum_value| yield enum_value if block_given? end end |
#new_enums_for_indexed_types ⇒ Object
106 107 108 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 106 def new_enums_for_indexed_types @@enums_for_indexed_types_new.call(@state) end |
#new_field_source(relationship_name:, field_path:) ⇒ Object
248 249 250 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 248 def new_field_source(relationship_name:, field_path:) @@field_source_new.call(relationship_name, field_path) end |
#new_filter_input_type(source_type, name_prefix: source_type, category: :filter_input) ⇒ Object
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 139 def new_filter_input_type(source_type, name_prefix: source_type, category: :filter_input) new_input_type(@state.type_ref(name_prefix).as_static_derived_type(category).name) do |t| t.documentation <<~EOS Input type used to specify filters on `#{source_type}` fields. Will be ignored if passed as an empty object (or as `null`). EOS t.field @state.schema_elements.any_of, "[#{t.name}!]" do |f| f.documentation <<~EOS Matches records where any of the provided sub-filters evaluate to true. This works just like an OR operator in SQL. Will be ignored when `null` is passed. When an empty list is passed, will cause this part of the filter to match no documents. EOS end t.field @state.schema_elements.not, t.name do |f| f.documentation <<~EOS Matches records where the provided sub-filter does not evaluate to true. This works just like a NOT operator in SQL. Will be ignored when `null` or an empty object is passed. EOS end yield t end end |
#new_graphql_sdl_enumerator(all_types_except_root_query_type) ⇒ Object
118 119 120 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 118 def new_graphql_sdl_enumerator(all_types_except_root_query_type) @@graphql_sdl_enumerator_new.call(@state, all_types_except_root_query_type) end |
#new_input_type(name) ⇒ Object
132 133 134 135 136 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 132 def new_input_type(name) @@input_type_new.call(@state, name) do |input_type| yield input_type end end |
#new_interface_type(name) ⇒ Object
203 204 205 206 207 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 203 def new_interface_type(name) @@interface_type_new.call(@state, name.to_s) do |interface_type| yield interface_type end end |
#new_object_type(name) ⇒ Object
210 211 212 213 214 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 210 def new_object_type(name) @@object_type_new.call(@state, name.to_s) do |object_type| yield object_type if block_given? end end |
#new_relationship(field, cardinality:, related_type:, foreign_key:, direction:) ⇒ Object
253 254 255 256 257 258 259 260 261 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 253 def new_relationship(field, cardinality:, related_type:, foreign_key:, direction:) @@relationship_new.call( field, cardinality: cardinality, related_type: , foreign_key: foreign_key, direction: direction ) end |
#new_scalar_type(name) ⇒ Object
217 218 219 220 221 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 217 def new_scalar_type(name) @@scalar_type_new.call(@state, name.to_s) do |scalar_type| yield scalar_type end end |
#new_sort_order_enum_value(enum_value, sort_order_field_path) ⇒ Object
224 225 226 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 224 def new_sort_order_enum_value(enum_value, sort_order_field_path) @@sort_order_enum_value_new.call(enum_value, sort_order_field_path) end |
#new_type_reference(name) ⇒ Object
229 230 231 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 229 def new_type_reference(name) @@type_reference_new.call(name, @state) end |
#new_type_with_subfields(schema_kind, name, wrapping_type:, field_factory:) ⇒ Object
234 235 236 237 238 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 234 def new_type_with_subfields(schema_kind, name, wrapping_type:, field_factory:) @@type_with_subfields_new.call(schema_kind, @state, name, wrapping_type: wrapping_type, field_factory: field_factory) do |type_with_subfields| yield type_with_subfields end end |
#new_union_type(name) ⇒ Object
241 242 243 244 245 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 241 def new_union_type(name) @@union_type_new.call(@state, name.to_s) do |union_type| yield union_type end end |