ibrain-core 0.1.0

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