graphoid 0.0.2 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +46 -32
  3. data/Rakefile +2 -6
  4. data/lib/graphoid/argument.rb +3 -0
  5. data/lib/graphoid/config.rb +2 -0
  6. data/lib/graphoid/definitions/filters.rb +7 -8
  7. data/lib/graphoid/definitions/inputs.rb +8 -6
  8. data/lib/graphoid/definitions/orders.rb +5 -5
  9. data/lib/graphoid/definitions/types.rb +46 -40
  10. data/lib/graphoid/drivers/active_record.rb +37 -37
  11. data/lib/graphoid/drivers/mongoid.rb +75 -53
  12. data/lib/graphoid/graphield.rb +6 -6
  13. data/lib/graphoid/grapho.rb +2 -0
  14. data/lib/graphoid/main.rb +6 -4
  15. data/lib/graphoid/mapper.rb +5 -2
  16. data/lib/graphoid/mutations/create.rb +12 -14
  17. data/lib/graphoid/mutations/delete.rb +12 -14
  18. data/lib/graphoid/mutations/processor.rb +3 -1
  19. data/lib/graphoid/mutations/structure.rb +2 -0
  20. data/lib/graphoid/mutations/update.rb +2 -0
  21. data/lib/graphoid/operators/attribute.rb +6 -5
  22. data/lib/graphoid/operators/inherited/belongs_to.rb +10 -8
  23. data/lib/graphoid/operators/inherited/embeds_many.rb +17 -3
  24. data/lib/graphoid/operators/inherited/embeds_one.rb +17 -3
  25. data/lib/graphoid/operators/inherited/has_many.rb +4 -2
  26. data/lib/graphoid/operators/inherited/has_one.rb +9 -7
  27. data/lib/graphoid/operators/inherited/many_to_many.rb +4 -2
  28. data/lib/graphoid/operators/relation.rb +8 -9
  29. data/lib/graphoid/queries/operation.rb +2 -0
  30. data/lib/graphoid/queries/processor.rb +7 -5
  31. data/lib/graphoid/queries/queries.rb +16 -17
  32. data/lib/graphoid/scalars.rb +28 -26
  33. data/lib/graphoid/utils.rb +6 -4
  34. data/lib/graphoid/version.rb +3 -1
  35. data/lib/graphoid.rb +2 -0
  36. metadata +15 -28
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5f060ce3dc46c8bf367aeec184daa225ebbe3d33471a37492f668b61a917e23d
4
- data.tar.gz: a62c8e9a0abd9c10b0a8842bdb5ca75a8cf8050fba088db794f723376a966f27
3
+ metadata.gz: 991348c4876e83ff75d3416a84f61cb1cd7d12773efb2cdeff71866ca16174ed
4
+ data.tar.gz: 95337e84c31b8e0feded9ab67fc7291d8e0269590602975c37473015aa19f182
5
5
  SHA512:
6
- metadata.gz: d183869e672db7ccb37a72e9c3fea49db92add12130e4a59c1e1cfa3f69d04db60fcad8141e99f0b8167ab2b3eb068eefde7c768be71e096001fd5117af7f470
7
- data.tar.gz: f5f2fa9be4b6052c10b036f9b5bc2f9c9dd59025b14aa2ecdf09c9885072686b3ebe09442dd47daf842c888a7323c4c52ce2b2717acad15f136e0544af9bb4be
6
+ metadata.gz: 5cdf65fa4c22ac0a01ec2671152886c4335940aeebf729a60bb83fc0445af18902f1c1c9e79cf5bfb0bb097778b38baeac09aa2c1ae2dc8429ea2bc8f8efd3f1
7
+ data.tar.gz: 9522be43b81979348c9ebfc2a319d62d7fc7343b2c66db8bccc04ee7549fa6fa309aa1b63317d7ccfff0bc81634dddf1a3dc826777cc05f6c5b938202c02c7c0
data/README.md CHANGED
@@ -1,62 +1,76 @@
1
1
  # Graphoid
2
- This gem is used to generate a full GraphQL api using introspection of MongoId models.
2
+ This gem is used to generate a full GraphQL API using introspection of Mongoid or ActiveRecord models.
3
+ After installing it, you will have create, update, delete, and query actions on any rails models you want.
3
4
 
4
5
  ## Dependency
5
6
  This gem depends on the graphql gem for rails https://github.com/rmosolgo/graphql-ruby
6
- So it is required to have it and install it using
7
- ```bash
8
- rails generate graphql:install
9
- ```
7
+ So please install that gem first before continuing
10
8
 
11
- ## Usage
12
- Require all the models in which you want to have basic find one, find many, create, update and delete actions on.
9
+ ## Installation
10
+ Add this line to your Gemfile:
11
+
12
+ ```ruby
13
+ gem 'graphoid'
14
+ ```
13
15
 
14
- Create the file `config/initializers/Graphoid.rb`
16
+ ```bash
17
+ $ bundle install
18
+ ```
15
19
 
16
- And require the models like this:
20
+ ## Configuration
21
+ Create the file `config/initializers/graphoid.rb`
22
+ And configure the database you want to use in it.
17
23
 
18
24
  ```ruby
19
25
  Graphoid.configure do |config|
20
26
  config.driver = :mongoid
27
+ # or
21
28
  config.driver = :active_record
22
29
  end
23
- Graphoid.initialize
24
30
  ```
25
31
 
26
- ## Installation
27
- Add this line to your application's Gemfile:
32
+ ## Usage
33
+ You can determine which models will be visible in the API by including the Graphoid Queries and Mutations
28
34
 
29
35
  ```ruby
30
- gem 'graphoid'
31
- ```
32
-
33
- And then execute:
34
- ```bash
35
- $ bundle
36
- ```
37
-
38
- Or install it yourself as:
39
- ```bash
40
- $ gem install graphoid
36
+ class Person
37
+ include Graphoid::Queries
38
+ include Graphoid::Mutations
39
+ end
41
40
  ```
42
41
 
43
- Then you can determine which queries and mutations should be created in `app/graphql/types/query_type.rb`
44
-
42
+ You can also include a special concern that will let you create virtual fields and forbid access to existing fields
45
43
  ```ruby
46
- include Graphoid::Queries
47
- include Graphoid::Mutations
48
- ```
44
+ class Person
45
+ include Graphoid::Graphield
49
46
 
50
- And which mutations should be created in `app/graphql/types/mutation_type.rb`
47
+ graphield :full_name, String # virtual fields need to resolve as a method
48
+ graphorbid :balance # attribute balance will not be exposed in the API
51
49
 
52
- ```ruby
53
- include Graphoid::Graphield
50
+ def full_name
51
+ "#{first_name} #{last_name}"
52
+ end
53
+ end
54
54
  ```
55
55
 
56
56
  ## Contributing
57
- Figure out the driver
58
57
  Functionality to sort top level models by association values
59
58
  Filter by Array or Hash => The cases are failing, implementation correction needed.
59
+ Revise TODOs in the code
60
+ Make a tutorial video
61
+ Fix Rubocop errors
62
+ Live Reload
63
+ AR eager load
64
+ Relation with aliases tests
65
+ Aggregations
66
+ Remove config / auto-setup AR-Mongo
67
+ Write division for "every" in Mongoid and AR
68
+ Permissions on fields
69
+ has_one_through implementation
70
+ has_many_selves (employee) tests
71
+ has_and_belongs_to_many_selves (followers) tests
72
+ Embedded::Many filtering implementation
73
+ Embedded::One filtering with OR/AND
60
74
 
61
75
  ## License
62
76
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'bundler/setup'
3
5
  rescue LoadError
@@ -14,11 +16,6 @@ RDoc::Task.new(:rdoc) do |rdoc|
14
16
  rdoc.rdoc_files.include('lib/**/*.rb')
15
17
  end
16
18
 
17
-
18
-
19
-
20
-
21
-
22
19
  require 'bundler/gem_tasks'
23
20
 
24
21
  require 'rake/testtask'
@@ -29,5 +26,4 @@ Rake::TestTask.new(:test) do |t|
29
26
  t.verbose = false
30
27
  end
31
28
 
32
-
33
29
  task default: :test
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Graphoid
4
+ # A module that defines the query packet arguments
2
5
  module Argument
3
6
  class << self
4
7
  def query_many(field, filter, order, required = {})
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Graphoid
2
4
  class << self
3
5
  attr_accessor :configuration
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Graphoid
2
4
  module Filters
3
-
4
5
  LIST = {}
5
6
 
6
7
  class << self
@@ -19,14 +20,14 @@ module Graphoid
19
20
  argument(:OR, -> { types[m] })
20
21
  argument(:AND, -> { types[m] })
21
22
 
22
- operators = ["lt", "lte", "gt", "gte", "contains", "not"]
23
- operators.push("regex") if Graphoid.configuration.driver == :mongoid
23
+ operators = %w[lt lte gt gte contains not]
24
+ operators.push('regex') if Graphoid.configuration.driver == :mongoid
24
25
 
25
26
  operators.each do |suffix|
26
27
  argument "#{name}_#{suffix}", type
27
28
  end
28
29
 
29
- ["in", "nin"].each do |suffix|
30
+ %w[in nin].each do |suffix|
30
31
  argument "#{name}_#{suffix}", types[type]
31
32
  end
32
33
  end
@@ -41,17 +42,15 @@ module Graphoid
41
42
  relation_name = Utils.camelize(name)
42
43
 
43
44
  if Relation.new(relation).many?
44
- ["some", "none", "every"].each do |suffix|
45
+ %w[some none every].each do |suffix|
45
46
  argument "#{relation_name}_#{suffix}", relation_filter
46
47
  end
47
48
  else
48
- argument "#{relation_name}", relation_filter
49
+ argument relation_name.to_s, relation_filter
49
50
  end
50
51
  end
51
-
52
52
  end
53
53
  end
54
-
55
54
  end
56
55
  end
57
56
  end
@@ -1,21 +1,23 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Graphoid
2
4
  module Inputs
3
5
  LIST = {}
4
6
 
5
7
  class << self
6
- def generate model
8
+ def generate(model)
7
9
  LIST[model] ||= GraphQL::InputObjectType.define do
8
10
  name = Utils.graphqlize(model.name)
9
11
  name("#{name}Input")
10
12
  description("Generated model input for #{name}")
11
13
 
12
14
  Attribute.fields_of(model).each do |field|
13
- unless field.name.start_with?("_")
14
- type = Graphoid::Mapper.convert(field)
15
- name = Utils.camelize(field.name)
15
+ next if field.name.start_with?('_')
16
16
 
17
- argument(name, type)
18
- end
17
+ type = Graphoid::Mapper.convert(field)
18
+ name = Utils.camelize(field.name)
19
+
20
+ argument(name, type)
19
21
  end
20
22
 
21
23
  Relation.relations_of(model).each do |name, relation|
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Graphoid
2
4
  module Orders
3
-
4
5
  LIST = {}
5
6
  @@enum_type = nil
6
7
 
@@ -31,13 +32,12 @@ module Graphoid
31
32
 
32
33
  def enum_type
33
34
  @@enum_type ||= GraphQL::EnumType.define do
34
- name "SortType"
35
+ name 'OrderType'
35
36
 
36
- value "ASC", "Ascendent"
37
- value "DESC", "Descendent"
37
+ value 'ASC', 'Ascendent'
38
+ value 'DESC', 'Descendent'
38
39
  end
39
40
  end
40
41
  end
41
42
  end
42
-
43
43
  end
@@ -1,15 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Graphoid
2
4
  module Types
3
-
4
5
  LIST = {}
5
6
  ENUMS = {}
6
7
 
7
8
  class << self
8
9
  def generate(model)
9
10
  Graphoid::Types::Meta ||= GraphQL::ObjectType.define do
10
- name("Meta")
11
- description("Meta Type")
12
- field("count", types.Int)
11
+ name('Meta')
12
+ description('Meta Type')
13
+ field('count', types.Int)
13
14
  end
14
15
 
15
16
  LIST[model] ||= GraphQL::ObjectType.define do
@@ -26,24 +27,24 @@ module Graphoid
26
27
  if _field.name.include?('_')
27
28
  define_method :"#{Utils.camelize(_field.name)}" do
28
29
  method_name = _field.name.to_s
29
- self[method_name] || self.send(method_name)
30
+ self[method_name] || send(method_name)
30
31
  end
31
32
  end
32
33
  end
33
34
  end
34
35
 
35
- Relation.relations_of(model).each do |name, relation|
36
+ Relation.relations_of(model).each do |_, relation|
36
37
  relation_class = relation.class_name.safe_constantize
37
38
 
38
39
  message = "in model #{model.name}: skipping relation #{relation.class_name}"
39
40
  unless relation_class
40
- STDERR.puts "Graphoid: warning: #{message} because the model name is not valid" if ENV['DEBUG']
41
+ warn "Graphoid: warning: #{message} because the model name is not valid" if ENV['DEBUG']
41
42
  next
42
43
  end
43
44
 
44
45
  relation_type = LIST[relation_class]
45
46
  unless relation_type
46
- STDERR.puts "Graphoid: warning: #{message} because it was not found as a model" if ENV['DEBUG']
47
+ warn "Graphoid: warning: #{message} because it was not found as a model" if ENV['DEBUG']
47
48
  next
48
49
  end
49
50
 
@@ -52,32 +53,32 @@ module Graphoid
52
53
  model.class_eval do
53
54
  if relation.name.to_s.include?('_')
54
55
  define_method :"#{name}" do
55
- self.send(relation.name)
56
+ send(relation.name)
56
57
  end
57
58
  end
58
59
  end
59
60
 
60
- if relation_type
61
- filter = Graphoid::Filters::LIST[relation_class]
62
- order = Graphoid::Orders::LIST[relation_class]
61
+ next unless relation_type
63
62
 
64
- if Relation.new(relation).many?
65
- plural_name = name.pluralize
63
+ filter = Graphoid::Filters::LIST[relation_class]
64
+ order = Graphoid::Orders::LIST[relation_class]
66
65
 
67
- field plural_name, types[relation_type] do
68
- Graphoid::Argument.query_many(self, filter, order)
69
- Graphoid::Types.resolve_many(self, relation_class, relation)
70
- end
66
+ if Relation.new(relation).many?
67
+ plural_name = name.pluralize
71
68
 
72
- field "_#{plural_name}_meta", Graphoid::Types::Meta do
73
- Graphoid::Argument.query_many(self, filter, order)
74
- Graphoid::Types.resolve_many(self, relation_class, relation)
75
- end
76
- else
77
- field name, relation_type do
78
- argument :where, filter
79
- Graphoid::Types.resolve_one(self, relation_class, relation)
80
- end
69
+ field plural_name, types[relation_type] do
70
+ Graphoid::Argument.query_many(self, filter, order)
71
+ Graphoid::Types.resolve_many(self, relation_class, relation)
72
+ end
73
+
74
+ field "_#{plural_name}_meta", Graphoid::Types::Meta do
75
+ Graphoid::Argument.query_many(self, filter, order)
76
+ Graphoid::Types.resolve_many(self, relation_class, relation)
77
+ end
78
+ else
79
+ field name, relation_type do
80
+ argument :where, filter
81
+ Graphoid::Types.resolve_one(self, relation_class, relation)
81
82
  end
82
83
  end
83
84
  end
@@ -85,26 +86,31 @@ module Graphoid
85
86
  end
86
87
 
87
88
  def resolve_one(field, model, association)
88
- field.resolve -> (obj, args, ctx) do
89
- filter = args["where"].to_h
89
+ field.resolve lambda { |obj, args, _ctx|
90
+ filter = args['where'].to_h
90
91
  result = obj.send(association.name)
91
- result = Graphoid::Queries::Processor.execute(model.where({ id: result.id }), filter).first if filter.present? && result
92
+ processor = Graphoid::Queries::Processor
93
+ if filter.present? && result
94
+ result = processor.execute(model.where(id: result.id), filter).first
95
+ end
92
96
  result
93
- end
97
+ }
94
98
  end
95
99
 
96
- def resolve_many(field, model, association)
97
- field.resolve -> (obj, args, ctx) do
98
- filter = args["where"].to_h
99
- order = args["order"].to_h
100
- limit = args["limit"]
101
- skip = args["skip"]
100
+ def resolve_many(field, _model, association)
101
+ field.resolve lambda { |obj, args, _ctx|
102
+ filter = args['where'].to_h
103
+ order = args['order'].to_h
104
+ limit = args['limit']
105
+ skip = args['skip']
106
+
107
+ processor = Graphoid::Queries::Processor
102
108
 
103
109
  result = obj.send(association.name)
104
- result = Graphoid::Queries::Processor.execute(result, filter) if filter.present?
110
+ result = processor.execute(result, filter) if filter.present?
105
111
 
106
112
  if order.present?
107
- order = Graphoid::Queries::Processor.parse_order(obj.send(association.name), order)
113
+ order = processor.parse_order(obj.send(association.name), order)
108
114
  result = result.order(order)
109
115
  end
110
116
 
@@ -112,7 +118,7 @@ module Graphoid
112
118
  result = result.skip(skip) if skip.present?
113
119
 
114
120
  result
115
- end
121
+ }
116
122
  end
117
123
  end
118
124
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Graphoid
2
4
  module ActiveRecordDriver
3
5
  class << self
@@ -21,33 +23,33 @@ module Graphoid
21
23
  type == ActiveRecord::Reflection::HasOneReflection
22
24
  end
23
25
 
24
- def embeds_one?(type)
26
+ def embeds_one?(_type)
25
27
  false
26
28
  end
27
29
 
28
- def embeds_many?(type)
30
+ def embeds_many?(_type)
29
31
  false
30
32
  end
31
33
 
32
- def embedded_in?(type)
34
+ def embedded_in?(_type)
33
35
  false
34
36
  end
35
37
 
36
38
  def types_map
37
39
  {
38
- binary: GraphQL::Types::Boolean,
40
+ binary: GraphQL::Types::Boolean,
39
41
  boolean: GraphQL::Types::Boolean,
40
- float: GraphQL::Types::Float,
42
+ float: GraphQL::Types::Float,
41
43
  integer: GraphQL::Types::Int,
42
- string: GraphQL::Types::String,
43
-
44
- datetime: Graphoid::Scalars::DateTime,
45
- date: Graphoid::Scalars::DateTime,
46
- time: Graphoid::Scalars::DateTime,
47
- timestamp: Graphoid::Scalars::DateTime,
48
- text: Graphoid::Scalars::Text,
49
- bigint: Graphoid::Scalars::BigInt,
50
- decimal: Graphoid::Scalars::Decimal
44
+ string: GraphQL::Types::String,
45
+
46
+ datetime: Graphoid::Scalars::DateTime,
47
+ date: Graphoid::Scalars::DateTime,
48
+ time: Graphoid::Scalars::DateTime,
49
+ timestamp: Graphoid::Scalars::DateTime,
50
+ text: Graphoid::Scalars::Text,
51
+ bigint: Graphoid::Scalars::BigInt,
52
+ decimal: Graphoid::Scalars::Decimal
51
53
  }
52
54
  end
53
55
 
@@ -99,17 +101,17 @@ module Graphoid
99
101
  def parse(attribute, value, operator)
100
102
  field = attribute.name
101
103
  case operator
102
- when "not"
103
- parsed = *["#{field} != ?", value]
104
- parsed = *["#{field} not like ?", "#{value}"] if attribute.type == :string
105
- parsed = *["#{field} is not null"] if value.nil?
106
- when "contains", "regex"
107
- parsed = *["#{field} like ?", "%#{value}%"]
108
- when "gt", "gte", "lt", "lte", "not", "in", "nin"
109
- operator = { gt: ">", gte: ">=", lt: "<", lte: "<=", in: "in", nin: "not in" }[operator.to_sym]
110
- parsed = *["#{field} #{operator} (?)", value]
104
+ when 'not'
105
+ parsed = ["#{field} != ?", value]
106
+ parsed = ["#{field} not like ?", value.to_s] if attribute.type == :string
107
+ parsed = ["#{field} is not null"] if value.nil?
108
+ when 'contains', 'regex'
109
+ parsed = ["#{field} like ?", "%#{value}%"]
110
+ when 'gt', 'gte', 'lt', 'lte', 'not', 'in', 'nin'
111
+ operator = { gt: '>', gte: '>=', lt: '<', lte: '<=', in: 'in', nin: 'not in' }[operator.to_sym]
112
+ parsed = ["#{field} #{operator} (?)", value]
111
113
  else
112
- parsed = *["#{field} = ?", value]
114
+ parsed = ["#{field} = ?", value]
113
115
  end
114
116
  parsed
115
117
  end
@@ -131,19 +133,19 @@ module Graphoid
131
133
  target = Graphoid::Queries::Processor.execute(relation.klass, value).to_a
132
134
 
133
135
  if relation.many_to_many?
134
- field_name = field_name.to_s.singularize + "_ids"
135
- ids = target.map(&(field_name.to_sym))
136
+ field_name = field_name.to_s.singularize + '_ids'
137
+ ids = target.map(&field_name.to_sym)
136
138
  ids.flatten!.uniq!
137
139
  else
138
140
  field_name = :"#{field_name}_id"
139
- ids = target.map(&(field_name))
141
+ ids = target.map(&field_name)
140
142
  end
141
143
 
142
- if operator == "none"
143
- parsed = *["id not in (?)", ids] if ids.present?
144
- elsif operator == "some"
145
- parsed = *["id in (?)", ids]
146
- elsif operator == "every"
144
+ if operator == 'none'
145
+ parsed = ['id not in (?)', ids] if ids.present?
146
+ elsif operator == 'some'
147
+ parsed = ['id in (?)', ids]
148
+ elsif operator == 'every'
147
149
 
148
150
  # the following process is a SQL division
149
151
  # the amount of queries it executes is on per row
@@ -157,7 +159,7 @@ module Graphoid
157
159
  operation = Operation.new(relation.klass, _key, _value)
158
160
  parsed = parse(operation.operand, operation.value, operation.operator)
159
161
  val = parsed.last.is_a?(String) ? "'#{parsed.last}'" : parsed.last
160
- parsed = parsed.first.sub("?", val)
162
+ parsed = parsed.first.sub('?', val)
161
163
  " AND #{parsed}"
162
164
  end.join
163
165
 
@@ -173,15 +175,13 @@ module Graphoid
173
175
  )
174
176
  "
175
177
  result = ActiveRecord::Base.connection.execute(query)
176
- ids = result.map{ |row| row["#{field_name}"] }
178
+ ids = result.map { |row| row[field_name.to_s] }
177
179
 
178
- parsed = *["id in (?)", ids]
180
+ parsed = ['id in (?)', ids]
179
181
  end
180
182
 
181
183
  parsed
182
184
  end
183
185
  end
184
-
185
186
  end
186
-
187
187
  end