rails-graphql-generator 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3044f10456b35da92ca114424e35da2ba575993f
4
+ data.tar.gz: 5701cb9b7a8883bb6021375ea1a3fc41483ac689
5
+ SHA512:
6
+ metadata.gz: 561be3a2e560815a94912380105d57c214d379a01e37637a93b9e93b1449fa6b0a728108c7de81dfe153048c975648ec4121a570587b69817aed1259a3a7a00c
7
+ data.tar.gz: b0c9d5a698de7bdb68be0387379c261598b17c577619e46f12bbeceb5a30ac652c255c87d82b4f7f9704a93dfee0917caa5c26c7ab870b4f81c19b43827c03b1
@@ -0,0 +1,9 @@
1
+ Description:
2
+
3
+
4
+ Example:
5
+ rails generate graphql Model
6
+
7
+ This will create:
8
+ app/graphql/types/model_type.rb
9
+ app/graphql/mutations/model_.rb
@@ -0,0 +1,23 @@
1
+ require 'generators/graphql/graphql_helpers'
2
+
3
+ module Graphql
4
+ module Generators
5
+ class CreateAllGenerator < Rails::Generators::Base
6
+ source_root File.expand_path('../templates', __FILE__)
7
+
8
+ include Graphql::Generators::GraphqlHelpers
9
+
10
+ def create
11
+ models.each { |model| create_type(model) }
12
+ models.each { |model| add_fields(model) }
13
+ models.each { |model| add_methods(model) }
14
+ models.each { |model| add_connections(model) }
15
+ models.each { |model| add_connection_query(model) }
16
+ models.each { |model| create_mutation(model) }
17
+ models.each { |model| add_fields_to_mutation(model) }
18
+ models.each { |model| add_mutation_query(model) }
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ require 'generators/graphql/graphql_helpers'
2
+
3
+ module Graphql
4
+ module Generators
5
+ class GraphqlGenerator < Rails::Generators::NamedBase
6
+ source_root File.expand_path('../templates', __FILE__)
7
+ namespace 'graphql'
8
+
9
+ include Graphql::Generators::GraphqlHelpers
10
+
11
+ def create
12
+ model = name.classify.constantize
13
+ create_type(model)
14
+ add_fields(model)
15
+ add_methods(model)
16
+ add_connections(model)
17
+ add_connection_query(model)
18
+ create_mutation(model)
19
+ add_fields_to_mutation(model)
20
+ add_mutation_query(model)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,256 @@
1
+ module Graphql
2
+ module Generators
3
+ module GraphqlHelpers
4
+ def models
5
+ @models ||= ActiveRecord::Base.connection.tables.map{ |x| x.classify.safe_constantize }.compact
6
+ end
7
+
8
+ def type_mapper
9
+ @type_mapper ||= {
10
+ integer: 'Int',
11
+ string: 'String',
12
+ datetime: 'String',
13
+ boolean: 'Boolean',
14
+ float: 'Float',
15
+ date: 'String',
16
+ json: 'String',
17
+ text: 'String',
18
+ decimal: 'Float',
19
+ '' => 'String',
20
+ }
21
+ end
22
+
23
+ def create_type(model)
24
+ create_file "app/graphql/types/#{ActiveModel::Naming.singular_route_key(model)}_type.rb", <<-FILE
25
+ #{model.to_s}Type = GraphQL::ObjectType.define do
26
+ name '#{model.to_s}'
27
+ description '#{model.to_s} type'
28
+
29
+ interfaces [NodeIdentification.interface]
30
+
31
+ field :id, field: GraphQL::Relay::GlobalIdField.new('#{model.to_s}')
32
+ # End of fields
33
+ end
34
+ FILE
35
+ add_fields(model)
36
+ end
37
+
38
+ def add_fields(model)
39
+ columns = model.columns_hash
40
+ columns.keys.each do |k|
41
+ next if columns[k].name == 'id'
42
+
43
+ if columns[k].type.present?
44
+ inject_into_file type_path(model), after: "field :id, field: GraphQL::Relay::GlobalIdField.new('#{model.to_s}')\n" do <<-FILE
45
+ field :#{columns[k].name}, types.#{type_mapper[columns[k].type]}
46
+ FILE
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ def add_methods(model)
53
+ associations = model.reflect_on_all_associations(:belongs_to)
54
+
55
+ associations.map(&:name).each do |ast|
56
+ association_klass = model.reflect_on_association(ast).class_name
57
+
58
+ begin
59
+ if models.include? association_klass.classify.constantize
60
+ if circular_finder[model.to_s].include? association_klass || association_klass == model.to_s
61
+ inject_into_file type_path(model), before: "# End of fields\n" do <<-FILE
62
+ field :#{ast.to_s} do
63
+ type -> { #{model.reflect_on_association(ast).class_name}Type }
64
+
65
+ resolve -> (#{singular_route_key(model)}, args, ctx) {
66
+ #{singular_route_key(model)}.#{ast.to_s}
67
+ }
68
+ end
69
+
70
+ FILE
71
+ end
72
+ else
73
+ inject_into_file type_path(model), before: "# End of fields\n" do <<-FILE
74
+ field :#{ast.to_s} do
75
+ type -> #{model.reflect_on_association(ast).class_name}Type
76
+
77
+ resolve -> (#{singular_route_key(model)}, args, ctx) {
78
+ #{singular_route_key(model)}.#{ast.to_s}
79
+ }
80
+ end
81
+ FILE
82
+ end
83
+ end
84
+ end
85
+ rescue => ex
86
+ puts ex
87
+ end
88
+ end
89
+ end
90
+
91
+ def add_connections(model)
92
+ associations = model.reflect_on_all_associations(:has_many)
93
+ associations += model.reflect_on_all_associations(:has_and_belongs_to_many)
94
+
95
+ associations.map(&:name).each do |ast|
96
+ association_klass = model.reflect_on_association(ast).class_name
97
+ begin
98
+ if models.include? association_klass.classify.constantize
99
+ if circular_finder[model.to_s].include? association_klass || association_klass == model.to_s
100
+ inject_into_file type_path(model), before: "# End of fields\n" do <<-FILE
101
+ connection :#{ast.to_s}, -> { #{model.reflect_on_association(ast).class_name}Type.connection_type } do
102
+ resolve ->(#{singular_route_key(model)}, args, ctx) {
103
+ #{singular_route_key(model)}.#{ast}
104
+ }
105
+ end
106
+
107
+ FILE
108
+ end
109
+ else
110
+ inject_into_file type_path(model), before: "# End of fields\n" do <<-FILE
111
+ connection :#{ast.to_s}, #{model.reflect_on_association(ast).class_name}Type.connection_type do
112
+ resolve -> (#{singular_route_key(model)}, args, ctx) {
113
+ #{singular_route_key(model)}.#{ast}
114
+ }
115
+ end
116
+ FILE
117
+ end
118
+ end
119
+ end
120
+ rescue => ex
121
+ puts ex
122
+ end
123
+ end
124
+ end
125
+
126
+ def add_connection_query(model)
127
+ inject_into_file 'app/graphql/types/root_level_type.rb', after: "field :id, field: GraphQL::Relay::GlobalIdField.new('RootLevel')\n" do <<-FILE
128
+ connection :#{model.to_s.tableize}, #{model.to_s}Type.connection_type do
129
+ resolve ->(object, args, ctx){
130
+ #{model.to_s}.all
131
+ }
132
+ end
133
+ FILE
134
+ end
135
+ end
136
+
137
+ def create_mutation(model)
138
+ create_file mutation_path(model), <<-FILE
139
+ module #{model.to_s}Mutations
140
+
141
+ Create = GraphQL::Relay::Mutation.define do
142
+ name 'Create#{model.to_s}'
143
+
144
+ return_field :#{instance_name(model)}, #{model.to_s}Type
145
+
146
+ resolve -> (inputs, ctx) {
147
+ root = RootLevel::STATIC
148
+ attr = inputs.keys.inject({}) do |memo, key|
149
+ memo[key] = inputs[key] unless key == "clientMutationId"
150
+ memo
151
+ end
152
+
153
+ #{instance_name(model)} = #{model.to_s}.create(attr)
154
+
155
+ { #{instance_name(model)}: #{instance_name(model)} }
156
+ }
157
+ end
158
+
159
+ Update = GraphQL::Relay::Mutation.define do
160
+ name 'Update#{model.to_s}'
161
+
162
+ input_field :id, !types.ID
163
+
164
+ return_field :#{instance_name(model)}, #{model.to_s}Type
165
+
166
+ resolve -> (inputs, ctx) {
167
+ #{instance_name(model)} = NodeIdentification.object_from_id((inputs[:id]), ctx)
168
+ attr = inputs.keys.inject({}) do |memo, key|
169
+ memo[key] = inputs[key] unless key == "clientMutationId" || key == 'id'
170
+ memo
171
+ end
172
+
173
+ #{instance_name(model)}.update(attr)
174
+ { #{instance_name(model)}: #{instance_name(model)} }
175
+ }
176
+ end
177
+
178
+ Destroy = GraphQL::Relay::Mutation.define do
179
+ name "Destroy#{model.to_s}"
180
+
181
+ input_field :id, !types.ID
182
+
183
+ resolve -> (inputs, ctx) {
184
+ #{instance_name(model)} = NodeIdentification.object_from_id((inputs[:id]), ctx)
185
+ #{instance_name(model)}.destroy
186
+ { }
187
+ }
188
+ end
189
+ end
190
+ FILE
191
+ end
192
+
193
+ def add_fields_to_mutation(model)
194
+ columns = model.columns_hash
195
+ columns.keys.each do |k|
196
+ next if %w(id created_at updated_at).include? columns[k].name
197
+ if columns[k].type.present?
198
+ inject_into_file mutation_path(model), after: "name 'Update#{model.to_s}'\n" do <<-FILE
199
+ input_field :#{columns[k].name}, types.#{type_mapper[columns[k].type]}
200
+ FILE
201
+ end
202
+
203
+ inject_into_file mutation_path(model), after: "name 'Create#{model.to_s}'\n", :force => true do <<-FILE
204
+ input_field :#{columns[k].name}, !types.#{type_mapper[columns[k].type]}
205
+ FILE
206
+ end
207
+ end
208
+ end
209
+ end
210
+
211
+ def add_mutation_query(model)
212
+ inject_into_file "app/graphql/types/mutation_type.rb", after: "name 'MutationType'\n" do <<-FILE
213
+ field :create#{model.to_s}, field: #{model.to_s}Mutations::Create.field
214
+ field :update#{model.to_s}, field: #{model.to_s}Mutations::Update.field
215
+ field :destroy#{model.to_s}, field: #{model.to_s}Mutations::Destroy.field
216
+
217
+ FILE
218
+ end
219
+ end
220
+
221
+ def singular_route_key(model)
222
+ ActiveModel::Naming.singular_route_key(model)
223
+ end
224
+
225
+ def mutation_path(model)
226
+ "app/graphql/mutations/#{singular_route_key(model)}_mutations.rb"
227
+ end
228
+
229
+ def type_path(model)
230
+ "app/graphql/types/#{singular_route_key(model)}_type.rb"
231
+ end
232
+
233
+ def instance_name(model)
234
+ ActiveModel::Naming.singular_route_key(model)
235
+ end
236
+
237
+ def circular_finder
238
+ circular_finder = {}
239
+
240
+ models.each do |m|
241
+ associations = m.reflect_on_all_associations.map(&:name)
242
+ circular_finder[m.to_s] = [] unless circular_finder[m.to_s].present?
243
+
244
+ associations.each do |k|
245
+ association_klass = m.reflect_on_association(k).class_name
246
+ circular_finder[association_klass] = [] unless circular_finder[association_klass].present?
247
+
248
+ circular_finder[association_klass] << m.to_s
249
+ end
250
+ end
251
+
252
+ @circular_finder ||= circular_finder
253
+ end
254
+ end
255
+ end
256
+ end
@@ -0,0 +1,39 @@
1
+ module Graphql
2
+ module Generators
3
+ class InitGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('../templates', __FILE__)
5
+
6
+ def add_gems
7
+ gem 'graphql'
8
+ gem 'graphql-relay'
9
+ gem 'graphiql-rails'
10
+ gem 'graphql-formatter'
11
+ end
12
+
13
+ def routes
14
+ route "post 'graphql' => 'graph_ql#execute'"
15
+ route "mount GraphiQL::Rails::Engine, at: '/graphiql', graphql_path: '/graphql'"
16
+ end
17
+
18
+ def autoload_paths
19
+ application do <<-'RUBY'
20
+ config.autoload_paths += Dir["#{config.root}/app/graphql/**/"]
21
+ RUBY
22
+ end
23
+ end
24
+
25
+ def copy_templates
26
+ copy_file 'controllers/graph_ql_controller.rb', 'app/controllers/graph_ql_controller.rb'
27
+ copy_file 'models/root_level.rb', 'app/models/root_level.rb'
28
+ copy_file 'graph/relay_schema.rb', 'app/graphql/relay_schema.rb'
29
+ copy_file 'graph/node_identification.rb', 'app/graphql/node_identification.rb'
30
+ copy_file 'graph/types/root_level_type.rb', 'app/graphql/types/root_level_type.rb'
31
+ copy_file 'graph/types/query_type.rb', 'app/graphql/types/query_type.rb'
32
+ copy_file 'graph/types/mutation_type.rb', 'app/graphql/types/mutation_type.rb'
33
+
34
+ copy_file 'config/graphiql.rb', 'config/initializers/graphiql.rb'
35
+ end
36
+ end
37
+ end
38
+ end
39
+
@@ -0,0 +1 @@
1
+ GraphiQL::Rails.config.query_params = true
@@ -0,0 +1,15 @@
1
+ class GraphQlController < ApplicationController
2
+ skip_before_filter :verify_authenticity_token
3
+
4
+ def execute
5
+ puts '-' * 100, GraphQLFormatter.new(params[:query]).to_s, '-' * 100
6
+ if params[:variables].present?
7
+ variables = JSON.parse(params[:variables])
8
+ else
9
+ variables = params[:variables]
10
+ end
11
+
12
+ render json: GraphQL::Query.new(RelaySchema, params[:query], variables: variables, debug: true).result
13
+ end
14
+
15
+ end
@@ -0,0 +1,15 @@
1
+ NodeIdentification = GraphQL::Relay::GlobalNodeIdentification.define do
2
+ # Given a UUID & the query context,
3
+ # return the corresponding application object
4
+ object_from_id -> (id, ctx) do
5
+ type_name, id = NodeIdentification.from_global_id(id)
6
+ # "Post" -> Post.find(id)
7
+ Object.const_get(type_name).find(id)
8
+ end
9
+
10
+ # Given an application object,
11
+ # return a GraphQL ObjectType to expose that object
12
+ type_from_object -> (object) do
13
+ (object.class.name + 'Type').constantize
14
+ end
15
+ end
@@ -0,0 +1,33 @@
1
+ RelaySchema = GraphQL::Schema.new(query: QueryType, mutation: MutationType)
2
+
3
+ module RelaySchemaHelpers
4
+ SCHEMA_DIR = Rails.root.join('app/assets/javascripts/relay/')
5
+ SCHEMA_PATH = File.join(SCHEMA_DIR, 'schema.json')
6
+
7
+ def explain
8
+ Rails.cache.fetch checksum do
9
+ RelaySchema.execute GraphQL::Introspection::INTROSPECTION_QUERY
10
+ end
11
+ end
12
+
13
+ def checksum
14
+ files = Dir["app/graph/**/*.rb"].reject { |f| File.directory?(f) }
15
+ content = files.map { |f| File.read(f) }.join
16
+ Digest::SHA256.hexdigest(content).to_s
17
+ end
18
+
19
+ def generate
20
+ FileUtils.mkdir_p SCHEMA_DIR
21
+ result = JSON.pretty_generate(RelaySchema.explain)
22
+ unless File.exists?(SCHEMA_PATH) && File.read(SCHEMA_PATH) == result
23
+ File.write(SCHEMA_PATH, result)
24
+ end
25
+ end
26
+
27
+ def remove
28
+ FileUtils.rm SCHEMA_PATH if File.exist? SCHEMA_PATH
29
+ end
30
+
31
+ end
32
+
33
+ RelaySchema.extend RelaySchemaHelpers
@@ -0,0 +1,3 @@
1
+ MutationType = GraphQL::ObjectType.define do
2
+ name 'MutationType'
3
+ end
@@ -0,0 +1,13 @@
1
+ # ...and a query root
2
+ QueryType = GraphQL::ObjectType.define do
3
+ name "Query"
4
+ description "The query root of this schema"
5
+
6
+ field :node, field: NodeIdentification.field
7
+
8
+ field :root, RootLevelType do
9
+ resolve -> (obj, args, ctx) { RootLevel::STATIC }
10
+ end
11
+
12
+ # End Of Queries
13
+ end
@@ -0,0 +1,8 @@
1
+ RootLevelType = GraphQL::ObjectType.define do
2
+ name 'RootLevel'
3
+ description 'Unassociated root object queries'
4
+
5
+ interfaces [NodeIdentification.interface]
6
+
7
+ field :id, field: GraphQL::Relay::GlobalIdField.new('RootLevel')
8
+ end
@@ -0,0 +1,7 @@
1
+ class RootLevel < Struct.new :id
2
+ STATIC = new(id: 'root').freeze
3
+
4
+ def self.find(_)
5
+ STATIC
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails-graphql-generator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Muhammet
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-05-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.2'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.2'
27
+ description: This generator will put the graphql files input your app folder
28
+ email:
29
+ - dilekmuhammet@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - lib/generators/graphql/USAGE
35
+ - lib/generators/graphql/create_all_generator.rb
36
+ - lib/generators/graphql/graphql_generator.rb
37
+ - lib/generators/graphql/graphql_helpers.rb
38
+ - lib/generators/graphql/init_generator.rb
39
+ - lib/generators/graphql/templates/config/graphiql.rb
40
+ - lib/generators/graphql/templates/controllers/graph_ql_controller.rb
41
+ - lib/generators/graphql/templates/graph/node_identification.rb
42
+ - lib/generators/graphql/templates/graph/relay_schema.rb
43
+ - lib/generators/graphql/templates/graph/types/mutation_type.rb
44
+ - lib/generators/graphql/templates/graph/types/query_type.rb
45
+ - lib/generators/graphql/templates/graph/types/root_level_type.rb
46
+ - lib/generators/graphql/templates/models/root_level.rb
47
+ homepage: https://github.com/movielala/rails-graphql-generator
48
+ licenses:
49
+ - MIT
50
+ metadata: {}
51
+ post_install_message:
52
+ rdoc_options: []
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ requirements: []
66
+ rubyforge_project:
67
+ rubygems_version: 2.5.1
68
+ signing_key:
69
+ specification_version: 4
70
+ summary: A rails generator for GrapphQL.
71
+ test_files: []