ibrain-core 0.1.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.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +75 -0
  4. data/Rakefile +7 -0
  5. data/app/controllers/concerns/ibrain_errors.rb +23 -0
  6. data/app/controllers/concerns/ibrain_handler.rb +42 -0
  7. data/app/controllers/ibrain/base_controller.rb +28 -0
  8. data/app/controllers/ibrain/graphql_controller.rb +55 -0
  9. data/app/graphql/ibrain/base_schema.rb +52 -0
  10. data/app/graphql/ibrain/extentions/default_value.rb +15 -0
  11. data/app/graphql/ibrain/interfaces/base_interface.rb +5 -0
  12. data/app/graphql/ibrain/interfaces/person_interface.rb +15 -0
  13. data/app/graphql/ibrain/interfaces/record_interface.rb +10 -0
  14. data/app/graphql/ibrain/lazy/base.rb +8 -0
  15. data/app/graphql/ibrain/loaders/association_loader.rb +61 -0
  16. data/app/graphql/ibrain/mutations/base_mutation.rb +35 -0
  17. data/app/graphql/ibrain/policies/base_policy.rb +45 -0
  18. data/app/graphql/ibrain/policies/graphql_policy.rb +8 -0
  19. data/app/graphql/ibrain/resolvers/base_aggregate.rb +9 -0
  20. data/app/graphql/ibrain/resolvers/base_resolver.rb +15 -0
  21. data/app/graphql/ibrain/types/aggregate_type.rb +9 -0
  22. data/app/graphql/ibrain/types/base_argument.rb +11 -0
  23. data/app/graphql/ibrain/types/base_connection.rb +16 -0
  24. data/app/graphql/ibrain/types/base_edge.rb +10 -0
  25. data/app/graphql/ibrain/types/base_enum.rb +8 -0
  26. data/app/graphql/ibrain/types/base_field.rb +15 -0
  27. data/app/graphql/ibrain/types/base_input_object.rb +9 -0
  28. data/app/graphql/ibrain/types/base_interface.rb +14 -0
  29. data/app/graphql/ibrain/types/base_node.rb +13 -0
  30. data/app/graphql/ibrain/types/base_object.rb +14 -0
  31. data/app/graphql/ibrain/types/base_query_type.rb +14 -0
  32. data/app/graphql/ibrain/types/base_scalar.rb +8 -0
  33. data/app/graphql/ibrain/types/base_union.rb +10 -0
  34. data/app/graphql/ibrain/types/filter_type.rb +8 -0
  35. data/app/graphql/ibrain/types/node_type.rb +11 -0
  36. data/app/graphql/ibrain/util/field_combiner.rb +13 -0
  37. data/app/graphql/ibrain/util/query_combiner.rb +13 -0
  38. data/app/graphql/mutations/insert_user.rb +18 -0
  39. data/app/models/ibrain/ability.rb +51 -0
  40. data/app/models/ibrain/application_record.rb +7 -0
  41. data/app/models/ibrain/base.rb +47 -0
  42. data/app/models/ibrain/concerns/ransackable_attributes.rb +22 -0
  43. data/app/models/ibrain/concerns/soft_deletable.rb +16 -0
  44. data/app/models/ibrain/concerns/user_api_authentication.rb +23 -0
  45. data/app/models/ibrain/concerns/user_methods.rb +25 -0
  46. data/app/models/ibrain/legacy_user.rb +19 -0
  47. data/app/models/ibrain/role.rb +14 -0
  48. data/app/models/ibrain/role_user.rb +18 -0
  49. data/config/initializers/friendly_id.rb +87 -0
  50. data/config/locales/en.yml +10 -0
  51. data/config/locales/jp.yml +10 -0
  52. data/config/locales/vi.yml +10 -0
  53. data/config/routes.rb +9 -0
  54. data/lib/generators/ibrain/graphql/core.rb +75 -0
  55. data/lib/generators/ibrain/graphql/mutation_generator.rb +58 -0
  56. data/lib/generators/ibrain/graphql/object_generator.rb +80 -0
  57. data/lib/generators/ibrain/graphql/resolver_generator.rb +33 -0
  58. data/lib/generators/ibrain/graphql/resolvers_generator.rb +59 -0
  59. data/lib/generators/ibrain/graphql/templates/aggregate.erb +10 -0
  60. data/lib/generators/ibrain/graphql/templates/mutation.erb +16 -0
  61. data/lib/generators/ibrain/graphql/templates/object.erb +11 -0
  62. data/lib/generators/ibrain/graphql/templates/resolver.erb +15 -0
  63. data/lib/generators/ibrain/graphql/templates/resolvers.erb +13 -0
  64. data/lib/generators/ibrain/graphql/type_generator.rb +101 -0
  65. data/lib/generators/ibrain/install/install_generator.rb +189 -0
  66. data/lib/generators/ibrain/install/templates/config/database.tt +23 -0
  67. data/lib/generators/ibrain/install/templates/config/initializers/cors.tt +25 -0
  68. data/lib/generators/ibrain/install/templates/config/initializers/ibrain.rb.tt +55 -0
  69. data/lib/generators/ibrain/install/templates/config/puma.tt +43 -0
  70. data/lib/generators/ibrain/install/templates/graphql/app_schema.rb.tt +4 -0
  71. data/lib/generators/ibrain/install/templates/graphql/types/mutation_type.rb.tt +10 -0
  72. data/lib/generators/ibrain/install/templates/graphql/types/query_type.rb.tt +13 -0
  73. data/lib/ibrain/app_configuration.rb +66 -0
  74. data/lib/ibrain/config.rb +5 -0
  75. data/lib/ibrain/core/class_constantizer.rb +41 -0
  76. data/lib/ibrain/core/controller_helpers/auth.rb +64 -0
  77. data/lib/ibrain/core/controller_helpers/current_host.rb +17 -0
  78. data/lib/ibrain/core/controller_helpers/response.rb +52 -0
  79. data/lib/ibrain/core/controller_helpers/strong_parameters.rb +21 -0
  80. data/lib/ibrain/core/engine.rb +16 -0
  81. data/lib/ibrain/core/environment.rb +17 -0
  82. data/lib/ibrain/core/environment_extension.rb +27 -0
  83. data/lib/ibrain/core/role_configuration.rb +72 -0
  84. data/lib/ibrain/core/validators/email.rb +23 -0
  85. data/lib/ibrain/core/version.rb +17 -0
  86. data/lib/ibrain/core/versioned_value.rb +73 -0
  87. data/lib/ibrain/core.rb +86 -0
  88. data/lib/ibrain/encryptor.rb +27 -0
  89. data/lib/ibrain/i18n.rb +17 -0
  90. data/lib/ibrain/logger.rb +23 -0
  91. data/lib/ibrain/permission_sets/base.rb +33 -0
  92. data/lib/ibrain/permission_sets/super_user.rb +11 -0
  93. data/lib/ibrain/permission_sets.rb +4 -0
  94. data/lib/ibrain/permitted_attributes.rb +26 -0
  95. data/lib/ibrain/preferences/configuration.rb +170 -0
  96. data/lib/ibrain/preferences/preferable.rb +183 -0
  97. data/lib/ibrain/preferences/preferable_class_methods.rb +140 -0
  98. data/lib/ibrain/user_class_handle.rb +29 -0
  99. data/lib/ibrain_core.rb +3 -0
  100. data/lib/tasks/ibrain/auto_annotate_models.rake +61 -0
  101. data/lib/tasks/ibrain/core_tasks.rake +5 -0
  102. data/lib/tasks/ibrain/ridgepole.rake +37 -0
  103. metadata +293 -0
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibrain
4
+ module Types
5
+ class BaseObject < GraphQL::Schema::Object
6
+ implements GraphQL::Relay::Node.interface
7
+
8
+ edge_type_class(Ibrain::Types::BaseEdge)
9
+ connection_type_class(Ibrain::Types::BaseConnection)
10
+
11
+ field_class ::Ibrain::Types::BaseField
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibrain
4
+ module Types
5
+ class BaseQueryType < Types::BaseObject
6
+ # Add `node(id: ID!) and `nodes(ids: [ID!]!)`
7
+ include GraphQL::Types::Relay::HasNodeField
8
+ include GraphQL::Types::Relay::HasNodesField
9
+
10
+ # Add root-level fields here.
11
+ # They will be entry points for queries on your schema.
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibrain
4
+ module Types
5
+ class BaseScalar < GraphQL::Schema::Scalar
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibrain
4
+ module Types
5
+ class BaseUnion < GraphQL::Schema::Union
6
+ edge_type_class(Ibrain::Types::BaseEdge)
7
+ connection_type_class(Ibrain::Types::BaseConnection)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibrain
4
+ module Types
5
+ class FilterType < GraphQL::Schema::Scalar
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibrain
4
+ module Types
5
+ module NodeType
6
+ include Ibrain::Types::BaseInterface
7
+ # Add the `id` field
8
+ include GraphQL::Types::Relay::NodeBehaviors
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibrain
4
+ module Util
5
+ class FieldCombiner
6
+ def self.combine(query_types)
7
+ Array(query_types).inject({}) do |acc, query_type|
8
+ acc.merge!(query_type.fields)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibrain
4
+ module Util
5
+ class QueryCombiner
6
+ def self.combine(query_types)
7
+ Array(query_types).inject({}) do |acc, query_type|
8
+ acc.merge!(query_type.fields)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibrain
4
+ module Mutations
5
+ class InsertUser < Ibrain::Mutations::BaseMutation
6
+ # TODO: define return fields
7
+ # field :post, Types::PostType, null: false
8
+
9
+ # TODO: define arguments
10
+ # argument :name, String, required: true
11
+
12
+ # TODO: define resolve method
13
+ # def resolve(name:)
14
+ # { post: ... }
15
+ # end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Implementation class for Cancan gem. Instead of overriding this class, consider adding new permissions
4
+ # using the special +register_ability+ method which allows extensions to add their own abilities.
5
+ #
6
+ # See http://github.com/ryanb/cancan for more details on cancan.
7
+ require 'cancan'
8
+ module Ibrain
9
+ class Ability
10
+ include CanCan::Ability
11
+
12
+ class_attribute :abilities
13
+ self.abilities = Set.new
14
+
15
+ attr_reader :user
16
+
17
+ # Allows us to go beyond the standard cancan initialize method which makes it difficult for engines to
18
+ # modify the default +Ability+ of an application. The +ability+ argument must be a class that includes
19
+ # the +CanCan::Ability+ module. The registered ability should behave properly as a stand-alone class
20
+ # and therefore should be easy to test in isolation.
21
+ def self.register_ability(ability)
22
+ abilities.add(ability)
23
+ end
24
+
25
+ def self.remove_ability(ability)
26
+ abilities.delete(ability)
27
+ end
28
+
29
+ def initialize(current_user)
30
+ @user = current_user || Ibrain.user_class.new
31
+
32
+ activate_permission_sets
33
+ register_extension_abilities
34
+ end
35
+
36
+ private
37
+
38
+ # Before, this was the only way to extend this ability. Permission sets have been added since.
39
+ # It is recommended to use them instead for extension purposes if possible.
40
+ def register_extension_abilities
41
+ Ability.abilities.each do |clazz|
42
+ ability = clazz.send(:new, user)
43
+ merge(ability)
44
+ end
45
+ end
46
+
47
+ def activate_permission_sets
48
+ Ibrain::Config.roles.activate_permissions! self, user
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibrain
4
+ class ApplicationRecord < ActiveRecord::Base
5
+ self.abstract_class = true
6
+ end
7
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Ibrain::Base < Ibrain::ApplicationRecord
4
+ include ActionView::Helpers::DateHelper
5
+
6
+ self.abstract_class = true
7
+
8
+ def self.adjust_date_for_cdt(datetime)
9
+ datetime.in_time_zone('UTC')
10
+ end
11
+
12
+ def string_id
13
+ try(:id).try(:to_s)
14
+ end
15
+
16
+ scope :graphql_ransack, lambda { |params|
17
+ ransack(params).result(distinct: true)
18
+ }
19
+
20
+ scope :reverse_scope, lambda {
21
+ order(created_at: :desc)
22
+ }
23
+
24
+ scope :ransack_query, lambda { |params, page, per_page = 10|
25
+ ransack(params).
26
+ result.
27
+ page(page).
28
+ per(per_page)
29
+ }
30
+
31
+ def cryptor
32
+ ::Ibrain::Encryptor.new
33
+ end
34
+
35
+ def created_in_word
36
+ time_ago_in_words(created_at)
37
+ end
38
+
39
+ # Provides a scope that should be included any time data
40
+ # are fetched with the intention of displaying to the user.
41
+ #
42
+ # Allows individual stores to include any active record scopes or joins
43
+ # when data are displayed.
44
+ def self.display_includes
45
+ where(nil)
46
+ end
47
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibrain::RansackableAttributes
4
+ extend ActiveSupport::Concern
5
+ included do
6
+ class_attribute :whitelisted_ransackable_associations
7
+ class_attribute :whitelisted_ransackable_attributes
8
+
9
+ class_attribute :default_ransackable_attributes
10
+ self.default_ransackable_attributes = %w[id]
11
+ end
12
+
13
+ class_methods do
14
+ def ransackable_associations(*_args)
15
+ whitelisted_ransackable_associations || []
16
+ end
17
+
18
+ def ransackable_attributes(*_args)
19
+ default_ransackable_attributes | (whitelisted_ransackable_attributes || [])
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'discard'
4
+
5
+ module Ibrain
6
+ module SoftDeletable
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ include Discard::Model
11
+ self.discard_column = :deleted_at
12
+
13
+ default_scope { kept }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibrain
4
+ module UserApiAuthentication
5
+ def generate_ibrain_api_key!
6
+ generate_ibrain_api_key
7
+ save!
8
+ end
9
+
10
+ def generate_ibrain_api_key
11
+ self.ibrain_api_key = SecureRandom.hex(24)
12
+ end
13
+
14
+ def clear_ibrain_api_key!
15
+ clear_ibrain_api_key
16
+ save!
17
+ end
18
+
19
+ def clear_ibrain_api_key
20
+ self.ibrain_api_key = nil
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibrain
4
+ module UserMethods
5
+ extend ActiveSupport::Concern
6
+
7
+ include Ibrain::UserApiAuthentication
8
+ include Ibrain::UserReporting
9
+
10
+ included do
11
+ extend Ibrain::DisplayMoney
12
+ after_create :auto_generate_ibrain_api_key
13
+
14
+ include Ibrain::RansackableAttributes unless included_modules.include?(Ibrain::RansackableAttributes)
15
+ end
16
+
17
+ def auto_generate_ibrain_api_key
18
+ return if !respond_to?(:ibrain_api_key) || ibrain_api_key.present?
19
+
20
+ if Ibrain::Config.generate_api_key_for_all_roles || (ibrain_roles.map(&:name) & Ibrain::Config.roles_for_auto_api_key).any?
21
+ generate_ibrain_api_key!
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibrain
4
+ # Default implementation of User.
5
+ #
6
+ # @note This class is intended to be modified by extensions (ex.
7
+ # ibrain-auth)
8
+ class LegacyUser < Ibrain::Base
9
+ include UserMethods
10
+
11
+ self.table_name = 'ibrain_users'
12
+
13
+ def self.model_name
14
+ ActiveModel::Name.new Ibrain::LegacyUser, Ibrain, 'user'
15
+ end
16
+
17
+ attr_accessor :password, :password_confirmation
18
+ end
19
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibrain
4
+ class Role < Ibrain::Base
5
+ has_many :role_users, class_name: "Ibrain::RoleUser", dependent: :destroy
6
+ has_many :users, through: :role_users
7
+
8
+ validates :name, uniqueness: { case_sensitive: true }
9
+
10
+ def admin?
11
+ name == "admin"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibrain
4
+ class RoleUser < Ibrain::Base
5
+ belongs_to :role, class_name: "Ibrain::Role", optional: true
6
+ belongs_to :user, class_name: Ibrain::UserClassHandle.new, optional: true
7
+
8
+ after_create :auto_generate_ibrain_api_key
9
+
10
+ validates :role_id, uniqueness: { scope: :user_id }
11
+
12
+ private
13
+
14
+ def auto_generate_ibrain_api_key
15
+ user.try(:auto_generate_ibrain_api_key)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ # FriendlyId Global Configuration
4
+ #
5
+ # Use this to set up shared configuration options for your entire application.
6
+ # Any of the configuration options shown here can also be applied to single
7
+ # models by passing arguments to the `friendly_id` class method or defining
8
+ # methods in your model.
9
+ #
10
+ # To learn more, check out the guide:
11
+ #
12
+ # http://norman.github.io/friendly_id/file.Guide.html
13
+
14
+ FriendlyId.defaults do |config|
15
+ # ## Reserved Words
16
+ #
17
+ # Some words could conflict with Rails's routes when used as slugs, or are
18
+ # undesirable to allow as slugs. Edit this list as needed for your app.
19
+ config.use :reserved
20
+
21
+ # ## Friendly Finders
22
+ #
23
+ # Uncomment this to use friendly finders in all models. By default, if
24
+ # you wish to find a record by its friendly id, you must do:
25
+ #
26
+ # MyModel.friendly.find('foo')
27
+ #
28
+ # If you uncomment this, you can do:
29
+ #
30
+ # MyModel.find('foo')
31
+ #
32
+ # This is significantly more convenient but may not be appropriate for
33
+ # all applications, so you must explicity opt-in to this behavior. You can
34
+ # always also configure it on a per-model basis if you prefer.
35
+ #
36
+ # Something else to consider is that using the :finders addon boosts
37
+ # performance because it will avoid Rails-internal code that makes runtime
38
+ # calls to `Module.extend`.
39
+ #
40
+ # config.use :finders
41
+ #
42
+ # ## Slugs
43
+ #
44
+ # Most applications will use the :slugged module everywhere. If you wish
45
+ # to do so, uncomment the following line.
46
+ #
47
+ # config.use :slugged
48
+ #
49
+ # By default, FriendlyId's :slugged addon expects the slug column to be named
50
+ # 'slug', but you can change it if you wish.
51
+ #
52
+ # config.slug_column = 'slug'
53
+ #
54
+ # When FriendlyId can not generate a unique ID from your base method, it appends
55
+ # a UUID, separated by a single dash. You can configure the character used as the
56
+ # separator. If you're upgrading from FriendlyId 4, you may wish to replace this
57
+ # with two dashes.
58
+ #
59
+ # config.sequence_separator = '-'
60
+ #
61
+ # ## Tips and Tricks
62
+ #
63
+ # ### Controlling when slugs are generated
64
+ #
65
+ # As of FriendlyId 5.0, new slugs are generated only when the slug field is
66
+ # nil, but if you're using a column as your base method can change this
67
+ # behavior by overriding the `should_generate_new_friendly_id` method that
68
+ # FriendlyId adds to your model. The change below makes FriendlyId 5.0 behave
69
+ # more like 4.0.
70
+ #
71
+ # config.use Module.new {
72
+ # def should_generate_new_friendly_id?
73
+ # slug.blank? || <your_column_name_here>_changed?
74
+ # end
75
+ # }
76
+ #
77
+ # FriendlyId uses Rails's `parameterize` method to generate slugs, but for
78
+ # languages that don't use the Roman alphabet, that's not usually suffient. Here
79
+ # we use the Babosa library to transliterate Russian Cyrillic slugs to ASCII. If
80
+ # you use this, don't forget to add "babosa" to your Gemfile.
81
+ #
82
+ # config.use Module.new {
83
+ # def normalize_friendly_id(text)
84
+ # text.to_slug.normalize! transliterations: [:russian, :latin]
85
+ # end
86
+ # }
87
+ end
@@ -0,0 +1,10 @@
1
+ en:
2
+ ibrain:
3
+ system:
4
+ message:
5
+ success: Fetch data successful
6
+ ok: Fetch data successful
7
+ errors:
8
+ session:
9
+ invalid_session: Login session is invalid. Please try again!
10
+ expired_session: Login session is expired.
@@ -0,0 +1,10 @@
1
+ jp:
2
+ ibrain:
3
+ system:
4
+ message:
5
+ success: Fetch data successful
6
+ ok: Fetch data successful
7
+ errors:
8
+ session:
9
+ invalid_session: Login session is invalid. Please try again!
10
+ expired_session: Login session is expired.
@@ -0,0 +1,10 @@
1
+ vi:
2
+ ibrain:
3
+ system:
4
+ message:
5
+ success: Lấy dữ liệu thành công
6
+ ok: Lấy dữ liệu thành công
7
+ errors:
8
+ session:
9
+ invalid_session: Phiên đăng nhập không hợp lệ. Vui lòng thử lại!
10
+ expired_session: Phiên đăng nhập đã hết hạn.
data/config/routes.rb ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ Ibrain::Core::Engine.routes.draw do
4
+ if ::Ibrain::Config.api_version.blank?
5
+ post '/api/graphql', to: 'graphql#execute'
6
+ else
7
+ post "/api/#{::Ibrain::Config.api_version.downcase}/graphql", controller: 'graphql', action: 'execute'
8
+ end
9
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators/base'
4
+
5
+ module Ibrain
6
+ module Graphql
7
+ module Core
8
+ def self.included(base)
9
+ base.send(
10
+ :class_option,
11
+ :directory,
12
+ type: :string,
13
+ default: "app/graphql",
14
+ desc: "Directory where generated files should be saved"
15
+ )
16
+ end
17
+
18
+ def insert_root_type(type, name)
19
+ log :add_root_type, type
20
+ sentinel = /< GraphQL::Schema\s*\n/m
21
+
22
+ in_root do
23
+ if File.exist?(schema_file_path)
24
+ inject_into_file schema_file_path, " #{type}(Types::#{name})\n", after: sentinel, verbose: false, force: false
25
+ end
26
+ end
27
+ end
28
+
29
+ def create_mutation_root_type
30
+ create_dir("#{options[:directory]}/mutations")
31
+ insert_root_type('mutation', 'MutationType')
32
+ end
33
+
34
+ def create_resolver_root_type
35
+ create_dir("#{options[:directory]}/resolvers")
36
+ insert_root_type('query', 'QueryType')
37
+ end
38
+
39
+ def schema_file_path
40
+ "#{options[:directory]}/#{schema_name.underscore}.rb"
41
+ end
42
+
43
+ def create_dir(dir)
44
+ empty_directory(dir)
45
+ if !options[:skip_keeps]
46
+ create_file("#{dir}/.keep")
47
+ end
48
+ end
49
+
50
+ def module_namespacing_when_supported(&block)
51
+ if defined?(module_namespacing)
52
+ module_namespacing(&block)
53
+ else
54
+ yield
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def schema_name
61
+ @schema_name ||= options[:schema] || "#{parent_name}Schema"
62
+ end
63
+
64
+ def parent_name
65
+ require File.expand_path("config/application", destination_root)
66
+
67
+ if Rails.application.class.respond_to?(:module_parent_name)
68
+ Rails.application.class.module_parent_name
69
+ else
70
+ Rails.application.class.parent_name
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'rails/generators/named_base'
5
+ require_relative 'core'
6
+
7
+ module Ibrain
8
+ module Graphql
9
+ # TODO: What other options should be supported?
10
+ #
11
+ # @example Generate a `GraphQL::Schema::RelayClassicMutation` by name
12
+ # rails g graphql:mutation CreatePostMutation
13
+ class MutationGenerator < Rails::Generators::Base
14
+ include Core
15
+
16
+ desc "Create a Relay Classic mutation by name"
17
+ source_root File.expand_path('templates', __dir__)
18
+
19
+ argument :name, type: :string
20
+ class_option :model, type: :string, default: nil
21
+
22
+ def initialize(args, *options) # :nodoc:
23
+ # Unfreeze name in case it's given as a frozen string
24
+ args[0] = args[0].dup if args[0].is_a?(String) && args[0].frozen?
25
+ super
26
+
27
+ assign_names!(name)
28
+ end
29
+
30
+ attr_reader :file_name, :mutation_name, :field_name, :model_name
31
+
32
+ def create_mutation_file
33
+ if @behavior == :revoke
34
+ log :gsub, "#{options[:directory]}/types/mutation_type.rb"
35
+ else
36
+ create_mutation_root_type
37
+ end
38
+
39
+ template "mutation.erb", "#{options[:directory]}/mutations/#{file_name}.rb"
40
+
41
+ sentinel = /class .*MutationType\s*<\s*[^\s]+?\n/m
42
+ in_root do
43
+ gsub_file "#{options[:directory]}/types/mutation_type.rb", / \# TODO: Add Mutations as fields\s*\n/m, ""
44
+ inject_into_file "#{options[:directory]}/types/mutation_type.rb", " field :#{field_name}, mutation: Mutations::#{mutation_name}\n", after: sentinel, verbose: false, force: false
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def assign_names!(name)
51
+ @field_name = name.camelize.underscore
52
+ @mutation_name = name.camelize(:upper)
53
+ @file_name = name.camelize.underscore
54
+ @model_name = options[:model].blank? ? 'Post' : options[:model].capitalize
55
+ end
56
+ end
57
+ end
58
+ end