graphql_rails 1.2.6 → 2.0.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4f7c503110a2423bb3e0b90154f9d0c4f6daeabc3315a1c49c1a832a29366ff2
4
- data.tar.gz: bfc7779f2678094724d0d1c3368edaa792665493a32d5112c5864998879fe96c
3
+ metadata.gz: 1d635269612ec07a541e7adebb01a9da7420f74511d8bbc4a30c60cea2eadc3f
4
+ data.tar.gz: 5d8e87e2d4c22bcf679645fa0de355b5c2a1765d357e28ba00560e83279462a0
5
5
  SHA512:
6
- metadata.gz: 740f486bb00615a7b83a78a8d5dc6d72d7d57dc0a42385178d1cc87875e264f0370f3131ca1e327f8d510f2126b7956915ead75374b06c81e99c96f98b6d57b9
7
- data.tar.gz: df08d0a84e9f505d25922d460677ddf2d34e5333258d111e9ee95090747612ccbb789d4f44d88f9cb1779a2afd4bbe4765b1ecabb1e88e2d75b777a05f94f3a4
6
+ metadata.gz: 4bdbac43bd69d1ace6bc7b29d6cfba07225950ff9a583f269310eed608fc5554afa934e2e34d4f2001349bdc8385fe4d94b6dc1b8844cb20f5d850276b5c9576
7
+ data.tar.gz: 9bdf1be49c697610f584def6bb016b5bd60455eb813c5b4884b491c39cb7df7c29d277efe56e45e79bc24089c3cf1befa9c178471396fe6eb6ca52c2ec49bffc
data/CHANGELOG.md CHANGED
@@ -9,6 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  * Added/Changed/Deprecated/Removed/Fixed/Security: YOUR CHANGE HERE
11
11
 
12
+ ## [2.0.0](2021-12-03)
13
+
14
+ * Added: support for generating multiple schema dumps with `rake graphql_rails:schema:dump`.
15
+ * Added: support for using chainable syntax for input attributes.
16
+ * Changed: changed default `predicate method type from `Boolean` to `Boolean!`
17
+ * Changed: changed error message when trying to paginate not supported types
18
+ * Added: support defining graphql-ruby types as strings.
19
+
12
20
  ## [1.2.4](2021-05-05)
13
21
 
14
22
  * Fixed: Dynamic types definition where type A references type B referencing type A.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- graphql_rails (1.2.6)
4
+ graphql_rails (2.0.0)
5
5
  activesupport (>= 4)
6
6
  graphql (~> 1.12, >= 1.12.4)
7
7
 
@@ -219,4 +219,4 @@ DEPENDENCIES
219
219
  simplecov
220
220
 
221
221
  BUNDLED WITH
222
- 2.1.4
222
+ 2.2.28
@@ -27,8 +27,8 @@ Most commonly you will use `attribute` to make your model methods and attributes
27
27
 
28
28
  Some types can be determined by attribute name, so you can skip this attribute:
29
29
 
30
- * attributes which ends with name `*_id` has `ID` type
31
- * attributes which ends with `?` has `Boolean` type
30
+ * attributes which ends with name `*_id` has `ID!` type
31
+ * attributes which ends with `?` has `Boolean!` type
32
32
  * all other attributes without type are considered to be `String`
33
33
 
34
34
  available types are:
@@ -45,9 +45,9 @@ class User
45
45
  include GraphqlRails::Model
46
46
 
47
47
  graphql do |c|
48
- c.attribute :shop_id # ID type
48
+ c.attribute :shop_id # ID! type
49
49
  c.attribute :full_name # String type
50
- c.attribute :admin? # Boolean type
50
+ c.attribute :admin? # Boolean! type
51
51
  c.attribute :level, type: 'integer'
52
52
  c.attribute :money, type: 'float'
53
53
  end
@@ -72,7 +72,7 @@ end
72
72
  class User
73
73
  include GraphqlRails::Model
74
74
 
75
- graphql.attribute :address, type: AddressType, required: true
75
+ graphql.attribute :address, type: 'AddressType!', required: true
76
76
  end
77
77
  ```
78
78
 
@@ -243,6 +243,24 @@ class User
243
243
  end
244
244
  ```
245
245
 
246
+ ### attribute.with
247
+
248
+ When you want to define some options dynamically, it's quite handy to use "Attribute#with" method:
249
+
250
+ ```ruby
251
+ class User
252
+ include GraphqlRails::Model
253
+
254
+ graphql do |c|
255
+ c.attribute(:shop_id).with(type: 'ID', description: 'references to shop')
256
+ # same as:
257
+ # c.attribute(:shop_id, type: 'ID', description: 'references to shop')
258
+ # also same as:
259
+ # c.attribute(:shop_id).type('ID').description('references to shop')
260
+ end
261
+ end
262
+ ```
263
+
246
264
  ### "attribute" configuration with chainable methods
247
265
 
248
266
  If your attribute definition is complex, you can define attribute in more eye-friendly chainable way with:
@@ -361,6 +379,22 @@ class User
361
379
  end
362
380
  ```
363
381
 
382
+ ### "input.attribute" configuration with chainable methods
383
+
384
+ If your input attribute definition is complex, you can define attribute in more eye-friendly chainable way with:
385
+
386
+ ```ruby
387
+ class User
388
+ include GraphqlRails::Model
389
+
390
+ graphql.input do |c|
391
+ c.attribute(:friends_count)
392
+ .type(:integer!)
393
+ .description('Can not be zero or less')
394
+ end
395
+ end
396
+ ```
397
+
364
398
  #### required type
365
399
 
366
400
  There are few ways how to mark field as required.
@@ -6,24 +6,24 @@ GraphqlRails includes rake task to allow creating schema snapshots easier:
6
6
  rake graphql_rails:schema:dump
7
7
  ```
8
8
 
9
- ## Dumping non default schema
9
+ ## Dumping only selected schema groups
10
10
 
11
- You can have multiple graphql schemas. Read more about this in [routes section](components/routes). In order to generate schema for one of groups, provide optional `name` argument:
11
+ You can specify which schema groups you want to dump. In order to do so, provide groups list as rake task argument and separate group names by comma:
12
12
 
13
13
  ```bash
14
- rake graphql_rails:schema:dump['your_group_name']
14
+ rake graphql_rails:schema:dump['your_group_name, your_group_name2']
15
15
  ```
16
16
 
17
- or using env variable `SCHEMA_GROUP_NAME`:
17
+ You can do this also by using ENV variable `SCHEMA_GROUP_NAME`:
18
18
 
19
19
  ```bash
20
- SCHEMA_GROUP_NAME=your_group_name rake graphql_rails:schema:dump
20
+ SCHEMA_GROUP_NAME="your_group_name, your_group_name2" rake graphql_rails:schema:dump
21
21
  ```
22
22
 
23
- ## Dumping schema in to non default path
23
+ ## Dumping schema in to non default folder
24
24
 
25
- By default schema will be dumped to `spec/fixtures/graphql_schema.graphql` path. If you want different schema path, add `GRAPHQL_SCHEMA_DUMP_PATH` env variable, like this:
25
+ By default schema will be dumped to `spec/fixtures` directory. If you want different schema path, add `GRAPHQL_SCHEMA_DUMP_DIR` env variable, like this:
26
26
 
27
27
  ```bash
28
- GRAPHQL_SCHEMA_DUMP_PATH='path/to/my/schema.graphql' rake graphql_rails:schema:dump
28
+ GRAPHQL_SCHEMA_DUMP_DIR='path/to/graphql/dumps' rake graphql_rails:schema:dump
29
29
  ```
@@ -6,14 +6,14 @@ require 'graphql_rails/attributes/attribute_name_parser'
6
6
  module GraphqlRails
7
7
  module Attributes
8
8
  # contains methods which are shared between various attribute-like classes
9
- # expects `initial_name` and `initial_type` to be defined
9
+ # expects `initial_name` and `type` to be defined
10
10
  module Attributable
11
11
  def field_name
12
12
  attribute_name_parser.field_name
13
13
  end
14
14
 
15
15
  def type_name
16
- @type_name ||= initial_type.to_s
16
+ type.to_s
17
17
  end
18
18
 
19
19
  def name
@@ -23,17 +23,9 @@ module GraphqlRails
23
23
  def required?
24
24
  return @required unless @required.nil?
25
25
 
26
- attribute_name_parser.required? || !initial_type.to_s[/!$/].nil? || initial_type.is_a?(GraphQL::Schema::NonNull)
27
- end
28
-
29
- def required
30
- @required = true
31
- self
32
- end
33
-
34
- def optional
35
- @required = false
36
- self
26
+ (type.nil? && attribute_name_parser.required?) ||
27
+ type.to_s[/!$/].present? ||
28
+ type.is_a?(GraphQL::Schema::NonNull)
37
29
  end
38
30
 
39
31
  def graphql_model
@@ -52,7 +44,7 @@ module GraphqlRails
52
44
 
53
45
  def type_parser
54
46
  @type_parser ||= begin
55
- type_for_parser = initial_type || attribute_name_parser.graphql_type
47
+ type_for_parser = type || attribute_name_parser.graphql_type
56
48
  TypeParser.new(type_for_parser, paginated: paginated?)
57
49
  end
58
50
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'graphql'
4
4
  require 'graphql_rails/attributes/attributable'
5
+ require 'graphql_rails/attributes/attribute_configurable'
5
6
  require 'graphql_rails/input_configurable'
6
7
 
7
8
  module GraphqlRails
@@ -9,47 +10,21 @@ module GraphqlRails
9
10
  # contains info about single graphql attribute
10
11
  class Attribute
11
12
  include Attributable
13
+ include AttributeConfigurable
12
14
  include InputConfigurable
13
15
 
14
16
  attr_reader :attributes
15
17
 
16
- # rubocop:disable Metrics/ParameterLists
17
- def initialize(name, type = nil, description: nil, property: name, required: nil, options: {})
18
- @initial_type = type
18
+ def initialize(name)
19
19
  @initial_name = name
20
- @options = options
21
- @description = description
22
- @property = property.to_s
23
- @required = required
20
+ @property = name.to_s
24
21
  @attributes ||= {}
25
22
  end
26
- # rubocop:enable Metrics/ParameterLists
27
23
 
28
- def type(new_type = nil)
29
- return @initial_type if new_type.nil?
24
+ def property(new_value = NOT_SET)
25
+ return @property if new_value == NOT_SET
30
26
 
31
- @initial_type = new_type
32
- self
33
- end
34
-
35
- def description(new_description = nil)
36
- return @description if new_description.nil?
37
-
38
- @description = new_description
39
- self
40
- end
41
-
42
- def property(new_property = nil)
43
- return @property if new_property.nil?
44
-
45
- @property = new_property.to_s
46
- self
47
- end
48
-
49
- def options(new_options = {})
50
- return @options if new_options.blank?
51
-
52
- @options = new_options
27
+ @property = new_value.to_s
53
28
  self
54
29
  end
55
30
 
@@ -82,7 +57,7 @@ module GraphqlRails
82
57
 
83
58
  protected
84
59
 
85
- attr_reader :initial_type, :initial_name
60
+ attr_reader :initial_name
86
61
 
87
62
  private
88
63
 
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'graphql_rails/attributes/type_parser'
4
+ require 'graphql_rails/attributes/attribute_name_parser'
5
+ require 'graphql_rails/model/build_enum_type'
6
+ require 'graphql_rails/concerns/chainable_options'
7
+ require 'active_support/concern'
8
+
9
+ module GraphqlRails
10
+ module Attributes
11
+ # Allows to set or get various attribute parameters
12
+ module AttributeConfigurable
13
+ extend ActiveSupport::Concern
14
+
15
+ included do
16
+ include GraphqlRails::ChainableOptions
17
+
18
+ chainable_option :description
19
+ chainable_option :options, default: {}
20
+ chainable_option :type
21
+ end
22
+
23
+ def required(new_value = true) # rubocop:disable Style/OptionalBooleanParameter
24
+ @required = new_value
25
+ self
26
+ end
27
+
28
+ def optional(new_value = true) # rubocop:disable Style/OptionalBooleanParameter
29
+ required(!new_value)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -5,12 +5,8 @@ module GraphqlRails
5
5
  # Parses attribute name and can generates graphql scalar type,
6
6
  # grapqhl name and etc. based on that
7
7
  class AttributeNameParser
8
- attr_reader :name
9
-
10
8
  def initialize(original_name, options: {})
11
- name = original_name.to_s
12
- @required = !name['!'].nil?
13
- @name = name.tr('!', '')
9
+ @original_name = original_name.to_s
14
10
  @options = options
15
11
  end
16
12
 
@@ -36,12 +32,16 @@ module GraphqlRails
36
32
  end
37
33
 
38
34
  def required?
39
- @required
35
+ original_name['!'].present? || original_name.end_with?('?')
36
+ end
37
+
38
+ def name
39
+ @name ||= original_name.tr('!', '')
40
40
  end
41
41
 
42
42
  private
43
43
 
44
- attr_reader :options
44
+ attr_reader :options, :original_name
45
45
 
46
46
  def original_format?
47
47
  options[:input_format] == :original || options[:attribute_name_format] == :original
@@ -4,22 +4,19 @@ module GraphqlRails
4
4
  module Attributes
5
5
  # contains info about single graphql input attribute
6
6
  class InputAttribute
7
+ require 'graphql_rails/model/build_enum_type'
7
8
  require_relative './input_type_parser'
8
9
  require_relative './attribute_name_parser'
9
10
  include Attributable
11
+ include AttributeConfigurable
10
12
 
11
- attr_reader :description
13
+ chainable_option :subtype
14
+ chainable_option :enum
12
15
 
13
- # rubocop:disable Metrics/ParameterLists
14
- def initialize(name, type: nil, description: nil, subtype: nil, required: nil, options: {})
16
+ def initialize(name, config:)
17
+ @config = config
15
18
  @initial_name = name
16
- @initial_type = type
17
- @description = description
18
- @options = options
19
- @subtype = subtype
20
- @required = required
21
19
  end
22
- # rubocop:enable Metrics/ParameterLists
23
20
 
24
21
  def input_argument_args
25
22
  type = raw_input_type || input_type_parser.input_type_arg
@@ -37,7 +34,7 @@ module GraphqlRails
37
34
 
38
35
  private
39
36
 
40
- attr_reader :initial_name, :initial_type, :options, :subtype
37
+ attr_reader :initial_name, :config
41
38
 
42
39
  def attribute_name_parser
43
40
  @attribute_name_parser ||= AttributeNameParser.new(
@@ -51,14 +48,23 @@ module GraphqlRails
51
48
 
52
49
  def input_type_parser
53
50
  @input_type_parser ||= begin
54
- initial_parseable_type = initial_type || attribute_name_parser.graphql_type
51
+ initial_parseable_type = type || enum_type || attribute_name_parser.graphql_type
55
52
  InputTypeParser.new(initial_parseable_type, subtype: subtype)
56
53
  end
57
54
  end
58
55
 
56
+ def enum_type
57
+ return if enum.blank?
58
+
59
+ ::GraphqlRails::Model::BuildEnumType.call(
60
+ "#{config.name}_#{initial_name}_enum",
61
+ allowed_values: enum
62
+ )
63
+ end
64
+
59
65
  def raw_input_type
60
- return initial_type if initial_type.is_a?(GraphQL::InputObjectType)
61
- return initial_type.graphql_input_type if initial_type.is_a?(Model::Input)
66
+ return type if type.is_a?(GraphQL::InputObjectType)
67
+ return type.graphql_input_type if type.is_a?(Model::Input)
62
68
  end
63
69
  end
64
70
  end
@@ -15,12 +15,6 @@ module GraphqlRails
15
15
  @subtype = subtype
16
16
  end
17
17
 
18
- def graphql_type
19
- return nil if unparsed_type.nil?
20
-
21
- partly_parsed_type || parsed_type
22
- end
23
-
24
18
  def input_type_arg
25
19
  if list?
26
20
  list_type_arg
@@ -34,7 +28,11 @@ module GraphqlRails
34
28
  attr_reader :unparsed_type, :subtype
35
29
 
36
30
  def unwrapped_type
37
- raw_unwrapped_type || unwrapped_scalar_type || unwrapped_model_input_type || raise_not_supported_type_error
31
+ raw_unwrapped_type ||
32
+ unwrapped_scalar_type ||
33
+ unwrapped_model_input_type ||
34
+ graphql_type_object ||
35
+ raise_not_supported_type_error
38
36
  end
39
37
 
40
38
  def raw_unwrapped_type
@@ -58,25 +58,46 @@ module GraphqlRails
58
58
  end
59
59
 
60
60
  def core_scalar_type?
61
- unwrapped_scalar_type.in?(TYPE_MAPPING.values)
61
+ unwrapped_scalar_type.present?
62
62
  end
63
63
 
64
64
  def graphql_model
65
- type_class = \
66
- if unparsed_type.is_a?(Class) && unparsed_type < GraphqlRails::Model
67
- unparsed_type
68
- else
69
- nullable_inner_name.safe_constantize
70
- end
71
-
72
- return if type_class.nil?
73
- return unless type_class < GraphqlRails::Model
65
+ extract_type_class_if { |type| graphql_model?(type) }
66
+ end
74
67
 
75
- type_class
68
+ def graphql_type_object
69
+ type_object = extract_type_class_if { |type| graphql_type_object?(type) }
70
+ type_object || graphql_model&.graphql&.graphql_type
76
71
  end
77
72
 
78
73
  protected
79
74
 
75
+ def extract_type_class_if
76
+ return unparsed_type if yield(unparsed_type)
77
+
78
+ type_class = nullable_inner_name.safe_constantize
79
+ return type_class if yield(type_class)
80
+
81
+ nil
82
+ end
83
+
84
+ def graphql_model?(type_class)
85
+ type_class.is_a?(Class) && type_class < GraphqlRails::Model
86
+ end
87
+
88
+ def graphql_type_object?(type_class)
89
+ return false unless type_class.is_a?(Class)
90
+
91
+ type_class < GraphQL::Schema::Object || type_class < GraphQL::Schema::Scalar
92
+ end
93
+
94
+ def applicable_graphql_type?(type)
95
+ return false unless type.is_a?(Class)
96
+ return true if type < GraphqlRails::Model
97
+
98
+ false
99
+ end
100
+
80
101
  def unwrap_type(type)
81
102
  unwrappable = type
82
103
  unwrappable = unwrappable.of_type while wrapped_type?(unwrappable)
@@ -51,7 +51,9 @@ module GraphqlRails
51
51
  def paginated_type_arg
52
52
  return graphql_model.graphql.connection_type if graphql_model
53
53
 
54
- raise NotSupportedFeature, 'pagination is only supported for models which include GraphqlRails::Model'
54
+ error_message = "Unable to paginate #{unparsed_type.inspect}. " \
55
+ 'Pagination is only supported for models which include GraphqlRails::Model'
56
+ raise NotSupportedFeature, error_message
55
57
  end
56
58
 
57
59
  def list_type_arg
@@ -107,14 +109,7 @@ module GraphqlRails
107
109
  end
108
110
 
109
111
  def type_by_name
110
- unwrapped_scalar_type || unwrapped_model_type || raise_not_supported_type_error
111
- end
112
-
113
- def unwrapped_model_type
114
- type_class = graphql_model
115
- return unless type_class
116
-
117
- type_class.graphql.graphql_type
112
+ unwrapped_scalar_type || graphql_type_object || raise_not_supported_type_error
118
113
  end
119
114
  end
120
115
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphqlRails
4
+ # Allows defining methods chained way
5
+ module ChainableOptions
6
+ NOT_SET = Object.new
7
+
8
+ # nodoc
9
+ module ClassMethods
10
+ def chainable_option(option_name, default: nil)
11
+ define_method(option_name) do |value = NOT_SET|
12
+ get_or_set_chainable_option(option_name, value, default: default)
13
+ end
14
+ end
15
+ end
16
+
17
+ def self.included(base)
18
+ base.extend(ClassMethods)
19
+ end
20
+
21
+ def initialize_copy(other)
22
+ super
23
+ @chainable_option = other.instance_variable_get(:@chainable_option).dup
24
+ end
25
+
26
+ def with(**options)
27
+ options.each do |method_name, args|
28
+ send_args = [method_name]
29
+ send_args << args if method(method_name).parameters.present?
30
+ public_send(*send_args)
31
+ end
32
+ self
33
+ end
34
+
35
+ private
36
+
37
+ def fetch_chainable_option(option_name, *default, &block)
38
+ @chainable_option.fetch(option_name.to_sym, *default, &block)
39
+ end
40
+
41
+ def get_or_set_chainable_option(option_name, value = NOT_SET, default: nil)
42
+ @chainable_option ||= {}
43
+ return fetch_chainable_option(option_name, default) if value == NOT_SET
44
+
45
+ @chainable_option[option_name.to_sym] = value
46
+ self
47
+ end
48
+ end
49
+ end
@@ -42,11 +42,8 @@ module GraphqlRails
42
42
  end
43
43
 
44
44
  def build_input_attribute(name, options: {}, **other_options)
45
- Attributes::InputAttribute.new(
46
- name.to_s,
47
- options: input_attribute_options.merge(options),
48
- **other_options
49
- )
45
+ input_options = input_attribute_options.merge(options)
46
+ Attributes::InputAttribute.new(name.to_s, config: self).with(options: input_options, **other_options)
50
47
  end
51
48
  end
52
49
  end
@@ -2,14 +2,25 @@
2
2
 
3
3
  module GraphqlRails
4
4
  module Model
5
- # contains methods which are shared between various configurations
6
- # expects `default_name` to be defined
5
+ # Contains methods which are shared between various configurations.
6
+ #
7
+ # Expects `default_name` to be defined.
8
+ # Expects `build_attribute(attr_name)` method to be defined.
7
9
  module Configurable
10
+ require 'active_support/concern'
11
+ require 'graphql_rails/concerns/chainable_options'
12
+
13
+ extend ActiveSupport::Concern
14
+
15
+ included do
16
+ include GraphqlRails::ChainableOptions
17
+
18
+ chainable_option :description
19
+ end
20
+
8
21
  def initialize_copy(other)
9
22
  super
10
- @name = nil
11
23
  @type_name = nil
12
- @description = nil
13
24
  @attributes = other.attributes.transform_values(&:dup)
14
25
  end
15
26
 
@@ -17,18 +28,21 @@ module GraphqlRails
17
28
  @attributes ||= {}
18
29
  end
19
30
 
20
- def name(graphql_name = nil)
21
- @name = graphql_name if graphql_name
22
- @name || default_name
31
+ def name(*args)
32
+ get_or_set_chainable_option(:name, *args) || default_name
23
33
  end
24
34
 
25
35
  def type_name
26
36
  @type_name ||= "#{name.camelize}Type#{SecureRandom.hex}"
27
37
  end
28
38
 
29
- def description(new_description = nil)
30
- @description = new_description if new_description
31
- @description
39
+ def attribute(attribute_name, **attribute_options)
40
+ key = attribute_name.to_s
41
+
42
+ attributes[key] ||= build_attribute(attribute_name).tap do |new_attribute|
43
+ new_attribute.with(**attribute_options) unless attribute_options.empty?
44
+ yield(new_attribute) if block_given?
45
+ end
32
46
  end
33
47
  end
34
48
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  require 'graphql_rails/attributes'
4
4
  require 'graphql_rails/model/find_or_build_graphql_type'
5
- require 'graphql_rails/model/build_enum_type'
6
5
  require 'graphql_rails/model/input'
7
6
  require 'graphql_rails/model/configurable'
8
7
  require 'graphql_rails/model/build_connection_type'
@@ -11,6 +10,7 @@ module GraphqlRails
11
10
  module Model
12
11
  # stores information about model specific config, like attributes and types
13
12
  class Configuration
13
+ include ChainableOptions
14
14
  include Configurable
15
15
 
16
16
  def initialize(model_class)
@@ -27,16 +27,9 @@ module GraphqlRails
27
27
  def attribute(attribute_name, **attribute_options)
28
28
  key = attribute_name.to_s
29
29
 
30
- attributes[key] ||= Attributes::Attribute.new(attribute_name)
31
-
32
- attributes[key].tap do |attribute|
33
- attribute_options.each do |method_name, args|
34
- send_args = [method_name]
35
- send_args << args if attribute.method(method_name).parameters.present?
36
- attribute.public_send(*send_args)
37
- end
38
-
39
- yield(attribute) if block_given?
30
+ attributes[key] ||= build_attribute(attribute_name).tap do |new_attribute|
31
+ new_attribute.with(**attribute_options)
32
+ yield(new_attribute) if block_given?
40
33
  end
41
34
  end
42
35
 
@@ -79,6 +72,10 @@ module GraphqlRails
79
72
 
80
73
  attr_reader :model_class
81
74
 
75
+ def build_attribute(attribute_name)
76
+ Attributes::Attribute.new(attribute_name)
77
+ end
78
+
82
79
  def default_name
83
80
  @default_name ||= model_class.name.split('::').last
84
81
  end
@@ -37,29 +37,30 @@ module GraphqlRails
37
37
  end
38
38
 
39
39
  def add_fields_to_graphql_type
40
- AddFieldsToGraphqlType.call(klass: klass, attributes: attributes.values.select(&:scalar_type?))
40
+ scalar_attributes, dynamic_attributes = attributes.values.partition(&:scalar_type?)
41
41
 
42
- attributes.values.reject(&:scalar_type?).tap do |dynamic_attributes|
43
- find_or_build_dynamic_graphql_types(dynamic_attributes) do |name, description, attributes, type_name|
44
- self.class.call(
45
- name: name, description: description,
46
- attributes: attributes, type_name: type_name
47
- )
48
- end
49
- AddFieldsToGraphqlType.call(klass: klass, attributes: dynamic_attributes)
50
- end
42
+ AddFieldsToGraphqlType.call(klass: klass, attributes: scalar_attributes)
43
+ dynamic_attributes.each { |attribute| find_or_build_dynamic_type(attribute) }
44
+ AddFieldsToGraphqlType.call(klass: klass, attributes: dynamic_attributes)
51
45
  end
52
46
 
53
- def find_or_build_dynamic_graphql_types(dynamic_attributes)
54
- dynamic_attributes.each do |attribute|
55
- yield(
56
- attribute.graphql_model.graphql.name,
57
- attribute.graphql_model.graphql.description,
58
- attribute.graphql_model.graphql.attributes,
59
- attribute.graphql_model.graphql.type_name
60
- )
47
+ def find_or_build_dynamic_type(attribute)
48
+ graphql_model = attribute.graphql_model
49
+ if graphql_model
50
+ find_or_build_graphql_model_type(graphql_model)
51
+ else
52
+ AddFieldsToGraphqlType.call(klass: klass, attributes: [attribute])
61
53
  end
62
54
  end
55
+
56
+ def find_or_build_graphql_model_type(graphql_model)
57
+ self.class.call(
58
+ name: graphql_model.graphql.name,
59
+ description: graphql_model.graphql.description,
60
+ attributes: graphql_model.graphql.attributes,
61
+ type_name: graphql_model.graphql.type_name
62
+ )
63
+ end
63
64
  end
64
65
  end
65
66
  end
@@ -1,14 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'graphql_rails/model/build_graphql_input_type'
4
- require 'graphql_rails/model/configurable'
5
-
6
3
  module GraphqlRails
7
4
  module Model
8
5
  # stores information about model input specific config, like attributes and types
9
6
  class Input
7
+ require 'graphql_rails/concerns/chainable_options'
8
+ require 'graphql_rails/model/configurable'
9
+ require 'graphql_rails/model/build_graphql_input_type'
10
+
10
11
  include Configurable
11
12
 
13
+ chainable_option :enum
14
+
12
15
  def initialize(model_class, input_name_suffix)
13
16
  @model_class = model_class
14
17
  @input_name_suffix = input_name_suffix
@@ -20,34 +23,20 @@ module GraphqlRails
20
23
  )
21
24
  end
22
25
 
23
- def attribute(attribute_name, type: nil, enum: nil, **attribute_options)
24
- input_type = attribute_type(attribute_name, type: type, enum: enum, **attribute_options)
25
-
26
- attributes[attribute_name.to_s] = Attributes::InputAttribute.new(
27
- attribute_name, type: input_type, **attribute_options
28
- )
29
- end
30
-
31
26
  private
32
27
 
33
28
  attr_reader :input_name_suffix, :model_class
34
29
 
30
+ def build_attribute(attribute_name)
31
+ Attributes::InputAttribute.new(attribute_name, config: self)
32
+ end
33
+
35
34
  def default_name
36
35
  @default_name ||= begin
37
36
  suffix = input_name_suffix ? input_name_suffix.to_s.camelize : ''
38
37
  "#{model_class.name.split('::').last}#{suffix}Input"
39
38
  end
40
39
  end
41
-
42
- def attribute_type(attribute_name, type:, enum:, description: nil, **_other)
43
- return type unless enum
44
-
45
- BuildEnumType.call(
46
- "#{name}_#{attribute_name}_enum",
47
- allowed_values: enum,
48
- description: description
49
- )
50
- end
51
40
  end
52
41
  end
53
42
  end
@@ -6,7 +6,7 @@ module GraphqlRails
6
6
  class Router
7
7
  # Generic class for any type graphql action. Should not be used directly
8
8
  class Route
9
- attr_reader :name, :module_name, :on, :relative_path
9
+ attr_reader :name, :module_name, :on, :relative_path, :groups
10
10
 
11
11
  def initialize(name, to: '', on:, groups: nil, **options)
12
12
  @name = name.to_s.camelize(:lower)
@@ -43,7 +43,7 @@ module GraphqlRails
43
43
 
44
44
  private
45
45
 
46
- attr_reader :function, :groups
46
+ attr_reader :function
47
47
 
48
48
  def resolver
49
49
  @resolver ||= Controller::BuildControllerActionResolver.call(route: self)
@@ -7,51 +7,37 @@ module GraphqlRails
7
7
 
8
8
  class MissingGraphqlRouterError < GraphqlRails::Error; end
9
9
 
10
- attr_reader :name
11
-
12
10
  def self.call(**args)
13
11
  new(**args).call
14
12
  end
15
13
 
16
- def initialize(name:)
17
- @name = name
14
+ def initialize(group:, router:, dump_dir: nil)
15
+ @group = group
16
+ @router = router
17
+ @dump_dir = dump_dir
18
18
  end
19
19
 
20
20
  def call
21
- validate
22
21
  File.write(schema_path, schema.to_definition)
23
22
  end
24
23
 
25
24
  private
26
25
 
27
- def validate
28
- return if router
29
-
30
- error_message = \
31
- 'GraphqlRouter is missing. ' \
32
- 'Run `rails g graphql_rails:install` to build it'
33
- raise MissingGraphqlRouterError, error_message
34
- end
35
-
36
- def router
37
- @router ||= '::GraphqlRouter'.safe_constantize
38
- end
26
+ attr_reader :router, :group
39
27
 
40
28
  def schema
41
- @schema ||= ::GraphqlRouter.graphql_schema(name.presence)
29
+ @schema ||= router.graphql_schema(group.presence)
42
30
  end
43
31
 
44
32
  def schema_path
45
- ENV['GRAPHQL_SCHEMA_DUMP_PATH'] || default_schema_path
46
- end
33
+ FileUtils.mkdir_p(dump_dir)
34
+ file_name = group.present? ? "graphql_#{group}_schema.graphql" : 'graphql_schema.graphql'
47
35
 
48
- def default_schema_path
49
- schema_folder_path = Rails.root.join('spec', 'fixtures')
50
-
51
- FileUtils.mkdir_p(schema_folder_path)
52
- file_name = name.present? ? "graphql_#{name}_schema.graphql" : 'graphql_schema.graphql'
36
+ "#{dump_dir}/#{file_name}"
37
+ end
53
38
 
54
- schema_folder_path.join(file_name)
39
+ def dump_dir
40
+ @dump_dir ||= Rails.root.join('spec/fixtures').to_s
55
41
  end
56
42
  end
57
43
  end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'graphql_rails/tasks/dump_graphql_schema'
4
+
5
+ module GraphqlRails
6
+ # Generates graphql schema dump files
7
+ class DumpGraphqlSchemas
8
+ require 'graphql_rails/errors/error'
9
+
10
+ class MissingGraphqlRouterError < GraphqlRails::Error; end
11
+
12
+ def self.call(**args)
13
+ new(**args).call
14
+ end
15
+
16
+ def initialize(dump_dir:, groups: nil)
17
+ @groups = groups.presence
18
+ @dump_dir = dump_dir
19
+ end
20
+
21
+ def call
22
+ validate
23
+ return dump_default_schema if groups.empty?
24
+
25
+ groups.each { |group| dump_graphql_schema(group) }
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :dump_dir
31
+
32
+ def dump_default_schema
33
+ dump_graphql_schema('')
34
+ end
35
+
36
+ def dump_graphql_schema(group)
37
+ DumpGraphqlSchema.call(group: group, router: router, dump_dir: dump_dir)
38
+ end
39
+
40
+ def validate
41
+ return if router
42
+
43
+ error_message = \
44
+ 'GraphqlRouter is missing. ' \
45
+ 'Run `rails g graphql_rails:install` to build it'
46
+ raise MissingGraphqlRouterError, error_message
47
+ end
48
+
49
+ def router
50
+ @router ||= '::GraphqlRouter'.safe_constantize
51
+ end
52
+
53
+ def groups
54
+ @groups ||= router.routes.flat_map(&:groups).uniq
55
+ end
56
+ end
57
+ end
@@ -1,14 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'graphql_rails/tasks/dump_graphql_schema'
3
+ require 'graphql_rails/tasks/dump_graphql_schemas'
4
4
 
5
5
  namespace :graphql_rails do
6
6
  namespace :schema do
7
7
  desc 'Dump GraphQL schema'
8
- task(:dump, %i[name] => :environment) do |_, args|
9
- default_name = ENV.fetch('SCHEMA_GROUP_NAME', '')
10
- args.with_defaults(name: default_name)
11
- GraphqlRails::DumpGraphqlSchema.call(name: args.name)
8
+ task(dump: :environment) do |_, args|
9
+ groups_from_args = args.extras
10
+ groups_from_env = ENV['SCHEMA_GROUP_NAME'].to_s.split(',').map(&:strip)
11
+ groups = groups_from_args + groups_from_env
12
+ dump_dir = ENV.fetch('GRAPHQL_SCHEMA_DUMP_DIR') { Rails.root.join('spec/fixtures').to_s }
13
+
14
+ GraphqlRails::DumpGraphqlSchemas.call(groups: groups, dump_dir: dump_dir)
12
15
  end
13
16
  end
14
17
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GraphqlRails
4
- VERSION = '1.2.6'
4
+ VERSION = '2.0.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.6
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Povilas Jurčys
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-09-28 00:00:00.000000000 Z
11
+ date: 2021-12-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: graphql
@@ -174,12 +174,14 @@ files:
174
174
  - lib/graphql_rails/attributes.rb
175
175
  - lib/graphql_rails/attributes/attributable.rb
176
176
  - lib/graphql_rails/attributes/attribute.rb
177
+ - lib/graphql_rails/attributes/attribute_configurable.rb
177
178
  - lib/graphql_rails/attributes/attribute_name_parser.rb
178
179
  - lib/graphql_rails/attributes/input_attribute.rb
179
180
  - lib/graphql_rails/attributes/input_type_parser.rb
180
181
  - lib/graphql_rails/attributes/type_name_info.rb
181
182
  - lib/graphql_rails/attributes/type_parseable.rb
182
183
  - lib/graphql_rails/attributes/type_parser.rb
184
+ - lib/graphql_rails/concerns/chainable_options.rb
183
185
  - lib/graphql_rails/concerns/service.rb
184
186
  - lib/graphql_rails/controller.rb
185
187
  - lib/graphql_rails/controller/action.rb
@@ -226,6 +228,7 @@ files:
226
228
  - lib/graphql_rails/router/schema_builder.rb
227
229
  - lib/graphql_rails/rspec_controller_helpers.rb
228
230
  - lib/graphql_rails/tasks/dump_graphql_schema.rb
231
+ - lib/graphql_rails/tasks/dump_graphql_schemas.rb
229
232
  - lib/graphql_rails/tasks/schema.rake
230
233
  - lib/graphql_rails/version.rb
231
234
  homepage: https://github.com/samesystem/graphql_rails