graphql_rails 1.2.6 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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