active_graphql 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.hound.yml +4 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +48 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +7 -0
  8. data/CHANGELOG.md +25 -0
  9. data/CODE_OF_CONDUCT.md +74 -0
  10. data/Gemfile +15 -0
  11. data/Gemfile.lock +134 -0
  12. data/LICENSE.txt +21 -0
  13. data/Rakefile +6 -0
  14. data/active_graphql.gemspec +49 -0
  15. data/bin/console +14 -0
  16. data/bin/setup +8 -0
  17. data/docs/.nojekyll +0 -0
  18. data/docs/README.md +95 -0
  19. data/docs/_sidebar.md +4 -0
  20. data/docs/client.md +69 -0
  21. data/docs/index.html +70 -0
  22. data/docs/model.md +464 -0
  23. data/lib/active_graphql.rb +10 -0
  24. data/lib/active_graphql/client.rb +38 -0
  25. data/lib/active_graphql/client/actions.rb +15 -0
  26. data/lib/active_graphql/client/actions/action.rb +116 -0
  27. data/lib/active_graphql/client/actions/action/format_inputs.rb +80 -0
  28. data/lib/active_graphql/client/actions/action/format_outputs.rb +40 -0
  29. data/lib/active_graphql/client/actions/mutation_action.rb +29 -0
  30. data/lib/active_graphql/client/actions/query_action.rb +23 -0
  31. data/lib/active_graphql/client/adapters.rb +10 -0
  32. data/lib/active_graphql/client/adapters/graphlient_adapter.rb +32 -0
  33. data/lib/active_graphql/client/response.rb +47 -0
  34. data/lib/active_graphql/errors.rb +11 -0
  35. data/lib/active_graphql/model.rb +174 -0
  36. data/lib/active_graphql/model/action_formatter.rb +96 -0
  37. data/lib/active_graphql/model/build_or_relation.rb +66 -0
  38. data/lib/active_graphql/model/configuration.rb +83 -0
  39. data/lib/active_graphql/model/find_in_batches.rb +54 -0
  40. data/lib/active_graphql/model/relation_proxy.rb +321 -0
  41. data/lib/active_graphql/version.rb +5 -0
  42. metadata +254 -0
@@ -0,0 +1,321 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveGraphql
4
+ module Model
5
+ # transforms all AR-like queries in to graphql requests
6
+ class RelationProxy # rubocop:disable Metrics/ClassLength
7
+ require 'active_support/core_ext/module/delegation'
8
+ require 'active_graphql/model/find_in_batches'
9
+ require 'active_graphql/model/build_or_relation'
10
+ require 'active_graphql/errors'
11
+
12
+ DEFAULT_BATCH_SIZE = 100
13
+
14
+ include Enumerable
15
+
16
+ attr_reader :where_attributes, :output_values
17
+
18
+ delegate :each, :map, to: :to_a
19
+
20
+ def initialize(model, limit_number: nil, where_attributes: {}, offset_number: nil, meta_attributes: {}, order_attributes: [], output_values: [])
21
+ @model = model
22
+ @limit_number = limit_number
23
+ @where_attributes = where_attributes
24
+ @offset_number = offset_number
25
+ @meta_attributes = meta_attributes
26
+ @order_attributes = order_attributes
27
+ @output_values = output_values
28
+ end
29
+
30
+ def all
31
+ self
32
+ end
33
+
34
+ def limit(limit_number)
35
+ chain(limit_number: limit_number)
36
+ end
37
+
38
+ def offset(offset_number)
39
+ chain(offset_number: offset_number)
40
+ end
41
+
42
+ def where(new_where_attributes)
43
+ chain(where_attributes: where_attributes.deep_merge(new_where_attributes.symbolize_keys))
44
+ end
45
+
46
+ def select(*array_outputs, **hash_outputs)
47
+ full_array_outputs = (output_values + array_outputs).uniq
48
+ reselect(*full_array_outputs, **hash_outputs)
49
+ end
50
+
51
+ def reselect(*array_outputs, **hash_outputs)
52
+ outputs = join_array_and_hash(*array_outputs, **hash_outputs)
53
+ chain(output_values: outputs)
54
+ end
55
+
56
+ def select_attributes
57
+ output_values.presence || config.attributes_graphql_output
58
+ end
59
+
60
+ def merge(other_query)
61
+ where(other_query.where_attributes)
62
+ end
63
+
64
+ def unscope(where:)
65
+ chain(where_attributes: where_attributes.except(where))
66
+ end
67
+
68
+ def meta(new_meta_attributes)
69
+ chain(meta_attributes: meta_attributes.merge(new_meta_attributes.symbolize_keys))
70
+ end
71
+
72
+ def page(page_number = 1)
73
+ paginate(page: page_number)
74
+ end
75
+
76
+ def paginate(page: nil, per_page: 100)
77
+ page_number = [page.to_i, 1].max
78
+ offset = (page_number - 1) * per_page
79
+ limit(per_page).offset(offset).meta(current_page: page_number, per_page: per_page)
80
+ end
81
+
82
+ def current_page
83
+ meta_attributes.fetch(:current_page, 1)
84
+ end
85
+
86
+ def total_pages
87
+ last_page = (total.to_f / meta_attributes.fetch(:per_page, 100)).ceil
88
+ [last_page, 1].max
89
+ end
90
+
91
+ def count
92
+ @size = begin
93
+ total_without_offset = [total - offset_number.to_i].max
94
+ [total_without_offset, limit_number].compact.min
95
+ end
96
+ end
97
+
98
+ def total
99
+ formatted_raw.reselect(:total).result.total
100
+ end
101
+
102
+ def size
103
+ @size ||= count
104
+ end
105
+
106
+ def find(id) # rubocop:disable Metrics/AbcSize
107
+ action = formatted_action(
108
+ graphql_client
109
+ .query(resource_name)
110
+ .select(*select_attributes)
111
+ .where(config.primary_key => id)
112
+ )
113
+
114
+ response = action.response
115
+ raise Errors::RecordNotFoundError unless action.response.result
116
+
117
+ model.new(response.result!.to_h)
118
+ end
119
+
120
+ def last(number_of_items = 1)
121
+ paginated_raw = formatted_action(raw.meta(paginated: true))
122
+ result = paginated_raw.where(last: number_of_items).result!
123
+ collection = decorate_paginated_result(result)
124
+
125
+ number_of_items == 1 ? collection.first : collection
126
+ end
127
+
128
+ def first(number_of_items = 1)
129
+ paginated_raw = formatted_action(raw.meta(paginated: true))
130
+ result = paginated_raw.where(first: number_of_items).result!
131
+ collection = decorate_paginated_result(result)
132
+
133
+ number_of_items == 1 ? collection.first : collection
134
+ end
135
+
136
+ def pluck(*attributes)
137
+ map do |record|
138
+ if attributes.count > 1
139
+ attributes.map { |attribute| record.public_send(attribute) }
140
+ else
141
+ record.public_send(attributes.first)
142
+ end
143
+ end
144
+ end
145
+
146
+ def find_each(batch_size: DEFAULT_BATCH_SIZE)
147
+ find_in_batches(batch_size: batch_size) do |items|
148
+ items.each { |item| yield(item) }
149
+ end
150
+ self
151
+ end
152
+
153
+ def find_in_batches(*args, &block)
154
+ FindInBatches.call(meta(paginated: true), *args, &block)
155
+ end
156
+
157
+ def to_a
158
+ return @to_a if defined?(@to_a)
159
+
160
+ list = []
161
+ find_in_batches { |batch| list += batch }
162
+
163
+ @to_a = list
164
+ end
165
+
166
+ def next_page?
167
+ raw_result.page_info.has_next_page
168
+ end
169
+
170
+ def first_batch
171
+ @first_batch ||= decorate_paginated_result(formatted_raw_response.result!)
172
+ end
173
+
174
+ def raw_result
175
+ formatted_raw_response.result
176
+ end
177
+
178
+ def graphql_params
179
+ {
180
+ filter: where_attributes.presence,
181
+ order: order_attributes.presence,
182
+ first: limit_number,
183
+ after: offset_number&.to_s
184
+ }.compact
185
+ end
186
+
187
+ def to_graphql
188
+ formatted_action(raw.meta(paginated: true)).to_graphql
189
+ end
190
+
191
+ def order(*order_params)
192
+ chain(order_attributes: order_params_attributes(order_params))
193
+ end
194
+
195
+ def order_params_attributes(order_params)
196
+ send(:order_attributes) + order_params.compact
197
+ .flat_map { |order_param| ordering_attributes(order_param) }
198
+ .select(&:compact)
199
+ end
200
+
201
+ def ordering_attributes(order_param)
202
+ if order_param.is_a?(Hash)
203
+ order_param.map { |param, direction| order_param_attributes(param, direction) }
204
+ else
205
+ order_param_attributes(order_param, :asc)
206
+ end
207
+ end
208
+
209
+ def order_param_attributes(order_by, direction)
210
+ {
211
+ by: order_by&.to_s&.upcase,
212
+ direction: direction&.to_s&.upcase,
213
+ __keyword_attributes: %i[by direction]
214
+ }
215
+ end
216
+
217
+ def empty?
218
+ count.zero?
219
+ end
220
+
221
+ def blank?
222
+ empty?
223
+ end
224
+
225
+ def present?
226
+ !blank?
227
+ end
228
+
229
+ def or(relation)
230
+ BuildOrRelation.call(self, relation)
231
+ end
232
+
233
+ def respond_to_missing?(method_name, *args, &block)
234
+ model.respond_to?(method_name) || super
235
+ end
236
+
237
+ def method_missing(method_name, *args, &block)
238
+ if model.respond_to?(method_name)
239
+ merge(model.public_send(method_name, *args, &block))
240
+ else
241
+ super
242
+ end
243
+ end
244
+
245
+ private
246
+
247
+ attr_reader :model, :limit_number, :offset_number, :meta_attributes, :order_attributes
248
+
249
+ def formatted_raw_response
250
+ @formatted_raw_response ||= formatted_raw.response
251
+ end
252
+
253
+ def raw
254
+ @raw ||= begin
255
+ graphql_client
256
+ .query(resource_plural_name)
257
+ .meta(meta_attributes)
258
+ .select(select_attributes)
259
+ .where(graphql_params)
260
+ end
261
+ end
262
+
263
+ def formatted_action(action)
264
+ config.formatter.call(action)
265
+ end
266
+
267
+ def formatted_raw
268
+ formatted_action(raw)
269
+ end
270
+
271
+ def decorate_paginated_result(result)
272
+ result.edges.map { |it| model.new(it.node.to_h) }
273
+ end
274
+
275
+ def resource_plural_name
276
+ @resource_plural_name ||= begin
277
+ name = config.resource_plural_name&.to_s || resource_name.pluralize
278
+ name
279
+ end
280
+ end
281
+
282
+ def resource_name
283
+ @resource_name ||= begin
284
+ name = config.resource_name&.to_s || model.name.demodulize
285
+ name
286
+ end
287
+ end
288
+
289
+ def chain(
290
+ limit_number: send(:limit_number),
291
+ where_attributes: send(:where_attributes),
292
+ meta_attributes: send(:meta_attributes),
293
+ offset_number: send(:offset_number),
294
+ order_attributes: send(:order_attributes),
295
+ output_values: send(:output_values)
296
+ )
297
+ self.class.new(
298
+ model,
299
+ limit_number: limit_number,
300
+ where_attributes: where_attributes,
301
+ meta_attributes: meta_attributes,
302
+ offset_number: offset_number,
303
+ order_attributes: order_attributes,
304
+ output_values: output_values
305
+ )
306
+ end
307
+
308
+ def graphql_client
309
+ config.graphql_client
310
+ end
311
+
312
+ def config
313
+ model.active_graphql
314
+ end
315
+
316
+ def join_array_and_hash(*array, **hash)
317
+ array + hash.map { |k, v| { k => v } }
318
+ end
319
+ end
320
+ end
321
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveGraphql
4
+ VERSION = '0.2.1'
5
+ end
metadata ADDED
@@ -0,0 +1,254 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_graphql
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Povilas Jurcys
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-03-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: graphql
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.9.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.9.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: graphlient
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 4.0.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 4.0.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: activemodel
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 3.0.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 3.0.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: webmock
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '10.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '10.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '3.0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3.0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rubocop
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - '='
144
+ - !ruby/object:Gem::Version
145
+ version: '0.75'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '='
151
+ - !ruby/object:Gem::Version
152
+ version: '0.75'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rubocop-performance
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: rubocop-rspec
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ description:
182
+ email:
183
+ - po.jurcys@gmail.com
184
+ executables: []
185
+ extensions: []
186
+ extra_rdoc_files: []
187
+ files:
188
+ - ".gitignore"
189
+ - ".hound.yml"
190
+ - ".rspec"
191
+ - ".rubocop.yml"
192
+ - ".ruby-version"
193
+ - ".travis.yml"
194
+ - CHANGELOG.md
195
+ - CODE_OF_CONDUCT.md
196
+ - Gemfile
197
+ - Gemfile.lock
198
+ - LICENSE.txt
199
+ - Rakefile
200
+ - active_graphql.gemspec
201
+ - bin/console
202
+ - bin/setup
203
+ - docs/.nojekyll
204
+ - docs/README.md
205
+ - docs/_sidebar.md
206
+ - docs/client.md
207
+ - docs/index.html
208
+ - docs/model.md
209
+ - lib/active_graphql.rb
210
+ - lib/active_graphql/client.rb
211
+ - lib/active_graphql/client/actions.rb
212
+ - lib/active_graphql/client/actions/action.rb
213
+ - lib/active_graphql/client/actions/action/format_inputs.rb
214
+ - lib/active_graphql/client/actions/action/format_outputs.rb
215
+ - lib/active_graphql/client/actions/mutation_action.rb
216
+ - lib/active_graphql/client/actions/query_action.rb
217
+ - lib/active_graphql/client/adapters.rb
218
+ - lib/active_graphql/client/adapters/graphlient_adapter.rb
219
+ - lib/active_graphql/client/response.rb
220
+ - lib/active_graphql/errors.rb
221
+ - lib/active_graphql/model.rb
222
+ - lib/active_graphql/model/action_formatter.rb
223
+ - lib/active_graphql/model/build_or_relation.rb
224
+ - lib/active_graphql/model/configuration.rb
225
+ - lib/active_graphql/model/find_in_batches.rb
226
+ - lib/active_graphql/model/relation_proxy.rb
227
+ - lib/active_graphql/version.rb
228
+ homepage: https://github.com/samesystem/active_graphql
229
+ licenses:
230
+ - MIT
231
+ metadata:
232
+ homepage_uri: https://github.com/samesystem/active_graphql
233
+ source_code_uri: https://github.com/samesystem/active_graphql
234
+ changelog_uri: https://github.com/samesystem/active_graphql/blob/v0.2.1/CHANGELOG.md
235
+ post_install_message:
236
+ rdoc_options: []
237
+ require_paths:
238
+ - lib
239
+ required_ruby_version: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - ">="
242
+ - !ruby/object:Gem::Version
243
+ version: '0'
244
+ required_rubygems_version: !ruby/object:Gem::Requirement
245
+ requirements:
246
+ - - ">="
247
+ - !ruby/object:Gem::Version
248
+ version: '0'
249
+ requirements: []
250
+ rubygems_version: 3.0.3
251
+ signing_key:
252
+ specification_version: 4
253
+ summary: Graphql client
254
+ test_files: []