graphoid 0.0.2 → 0.0.4

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 (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