graphql_lazy_load 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f712c10800162f814d8f7d170e10231adf25aa518aab8c08b5f7d6459a8b9efe
4
- data.tar.gz: 98fd3434f3a11930cdd5e7e87be35e03054d7652108f45ecf837cffb7deba177
3
+ metadata.gz: 724af5e1b3fe1f2fbff97284fe54f2b20d9ddd9e3dddd3932c2f5b913c678873
4
+ data.tar.gz: adfcdb624948134a38390d7470898d8d038982dd917d76dda82eb898f3ffaadd
5
5
  SHA512:
6
- metadata.gz: 70c960dc556b35794248ac96afcbb067067c8a2f07b1e72987d935afe3a9a7d4eb61bbd40024f3f9dffdb27c2e624caf023080c17ae8a2387fc1ee13db760fc5
7
- data.tar.gz: 13f918ac5f4d57099d5543c4fa48749ae69179c0026a71ecfef973cdbbcb61673fcbf6fb24bfbe403f6d2df6cdeb0de0b0326f538d47e7e6949a28fd4b3d18cf
6
+ metadata.gz: 81e56ea916ed8ae3173bd6629b037960f56f0edcb5031b64d7ad12a6c38ea1a293d98bca839955311adee539f300b1a4a7ab20a8a07ea3cc7394aaa0e2005705
7
+ data.tar.gz: 6062acf99ebc505293eb69bd5d917048f89927aa5be346cb8d86440fd70c05dbe370b193c007f1d00f3c6ef1a78d393a63955c3d393402432074316c2c776310
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- graphql_lazy_load (0.1.0)
4
+ graphql_lazy_load (0.2.0)
5
5
  activerecord (>= 4.1.16)
6
6
  graphql (>= 1.3.0)
7
7
 
@@ -38,7 +38,7 @@ GEM
38
38
  concurrent-ruby (1.1.5)
39
39
  crass (1.0.4)
40
40
  erubi (1.8.0)
41
- graphql (1.9.11)
41
+ graphql (1.9.12)
42
42
  i18n (1.6.0)
43
43
  concurrent-ruby (~> 1.0)
44
44
  jaro_winkler (1.5.3)
data/README.md CHANGED
@@ -6,7 +6,7 @@ Lazy executor for activerecord associations and graphql gem.
6
6
 
7
7
  GraphqlLazyLoad requires ActiveRecord >= 4.1.16 and Graphql >= 1.3.0. To use add this line to your application's Gemfile:
8
8
  ```ruby
9
- gem 'graphql_lazy_load'
9
+ gem 'graphql_lazy_load', '~> 0.2.0'
10
10
  ```
11
11
  Then run `bundle install`.
12
12
 
@@ -15,6 +15,7 @@ Or install it yourself as:
15
15
  $ gem install graphql_lazy_load
16
16
 
17
17
  ## Usage
18
+ ### ActiveRecordRelation
18
19
  To use, first add the executor (`GraphqlLazyLoad::ActiveRecordRelation`) to graphqls schema:
19
20
  ```ruby
20
21
  class MySchema < GraphQL::Schema
@@ -32,11 +33,37 @@ def field_name
32
33
  GraphqlLazyLoad::ActiveRecordRelation.new(self, :association_name)
33
34
  end
34
35
  ```
36
+ ### Custom
37
+ If you want to lazy load a non active record association you can use the `Custom` loader, first add the executor (`GraphqlLazyLoad::Custom`) to graphqls schema (both can be in the schema together):
38
+ ```ruby
39
+ class MySchema < GraphQL::Schema
40
+ mutation(Types::MutationType)
41
+ query(Types::QueryType)
42
+
43
+ lazy_resolve(GraphqlLazyLoad::Custom, :result)
44
+ end
45
+ ```
46
+
47
+ Now you can start using it! Wherever you have a something to lazy load, the syntax is:
48
+ ```ruby
49
+ field :field_name, Types::AssociationType, null: false
50
+ def field_name
51
+ GraphqlLazyLoad::Custom.new(self, :association_name, object.id) do |ids|
52
+ # return a hash with key matching object.id
53
+ AssociationName.where(id: ids).reduce({}) do |acc, value|
54
+ acc[value.id] = value
55
+ acc
56
+ end
57
+ end
58
+ end
59
+ ```
60
+ Values passed to the `Custom` initializer are `self`, `unique_identifier`, `unique_id` (gets passed to block, and is used to retrieve the values), you can also pass an optional value `params` (gets passed as second argument to block). It is important to note that data is grouped by `unique_identifier` and `params`.
35
61
  ## Examples
36
- If you have two models `Team` which can have many `Players`. To lazy load players from teams do the following:
62
+ If you have two models `Team` which can have many `Player`s. To lazy load players from teams do the following:
63
+ ### ActiveRecordRelation
37
64
  ```ruby
38
65
  module Types
39
- class PlayerType < Types::BaseObject
66
+ class TeamType < Types::BaseObject
40
67
  ...
41
68
  field :players, [Types::PlayerType], null: false
42
69
  def players
@@ -48,7 +75,7 @@ end
48
75
  And to lazy load teams from players do the following:
49
76
  ```ruby
50
77
  module Types
51
- class TeamType < Types::BaseObject
78
+ class PlayerType < Types::BaseObject
52
79
  ...
53
80
  field :team, Types::TeamType, null: false
54
81
  def team
@@ -57,8 +84,46 @@ module Types
57
84
  end
58
85
  end
59
86
  ```
87
+ ### Custom
88
+ ```ruby
89
+ module Types
90
+ class TeamType < Types::BaseObject
91
+ ...
92
+ field :players, [Types::PlayerType], null: false
93
+ def players
94
+ GraphqlLazyLoad::Custom.new(self, :players, object.id) do |team_ids|
95
+ # return a hash with key matching object.id
96
+ Player.where(team_id: team_ids).reduce({}) do |acc, player|
97
+ (acc[player.team_id] ||= []).push(player)
98
+ acc
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ ```
105
+ And to lazy load teams from players do the following:
106
+ ```ruby
107
+ module Types
108
+ class PlayerType < Types::BaseObject
109
+ ...
110
+ field :team, Types::TeamType, null: false
111
+ def team
112
+ GraphqlLazyLoad::Custom.new(self, :team, object.team_id) do |team_ids|
113
+ # return a hash with key matching object.team_id
114
+ Team.where(id: team_ids).reduce({}) do |acc, team|
115
+ acc[team.id] = team
116
+ acc
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ ```
123
+
60
124
  ### Scoping
61
- The great thing about `GraphqlLazyLoad::ActiveRecordRelation` is it allows scopes to be passed! So for the example above if you want to allow sorting (or query, paging, etc) do the following.
125
+ The great thing is you can pass scopes/params! So for the example above if you want to allow sorting (or query, paging, etc) do the following.
126
+ ### ActiveRecordRelation
62
127
  ```ruby
63
128
  module Types
64
129
  class PlayerType < Types::BaseObject
@@ -75,6 +140,27 @@ module Types
75
140
  end
76
141
  ```
77
142
 
143
+ ### Custom
144
+ ```ruby
145
+ module Types
146
+ class PlayerType < Types::BaseObject
147
+ ...
148
+ field :players, [Types::PlayerType], null: false do
149
+ argument :order, String, required: false
150
+ end
151
+ def players(order: nil)
152
+ GraphqlLazyLoad::Custom.new(self, :players, object.id, {order: order}) do |team_ids, params|
153
+ query = Player.where(team_id: team_ids)
154
+ query = query.order(params[:order].underscore) if params[:order]
155
+ query.reduce({}) do |acc, player|
156
+ (acc[player.team_id] ||= []).push(player)
157
+ acc
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+ ```
78
164
  To test this out try the example app at [graph_test](https://github.com/jonathongardner/graph_test)
79
165
 
80
166
  ## Contributing
Binary file
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GraphqlLazyLoad
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
@@ -4,6 +4,72 @@ require "graphql_lazy_load/version"
4
4
  require "active_record"
5
5
 
6
6
  module GraphqlLazyLoad
7
+ class Custom
8
+ def initialize(type, unique_identifier, value, **params, &block)
9
+ @unique_identifier = unique_identifier
10
+ @params = params
11
+ @block = block
12
+ @value = value
13
+ # Initialize the loading state for this query,
14
+ # or get the previously-initiated state
15
+ # scope cant be used as a hash key because when .hash is called on diff
16
+ # for ~same~ scopes its diff every time but scope == scope will return true if ~same~
17
+ @lazy = type.context[context_key] ||= {
18
+ values_to_load: Set.new,
19
+ ids: Set.new,
20
+ results: {}
21
+ }
22
+ # Register this to be loaded later unless we've already queued or loaded it
23
+ return if already_loaded_or_queued?
24
+ lazy_values.add(value)
25
+ lazy_ids.add(value)
26
+ end
27
+
28
+ # Return the loaded record, hitting the database if needed
29
+ def result
30
+ if !already_loaded? && any_to_load?
31
+ lazy_results.merge!(block_results)
32
+ lazy_values.clear
33
+ end
34
+ lazy_results[value]
35
+ end
36
+
37
+ private
38
+ attr_reader :unique_identifier, :params, :value
39
+
40
+ def block_results
41
+ @block.call(lazy_values, params)
42
+ end
43
+
44
+ def context_key
45
+ [unique_identifier, params]
46
+ end
47
+
48
+ def already_loaded_or_queued?
49
+ lazy_ids.include?(object_id)
50
+ end
51
+
52
+ def already_loaded?
53
+ lazy_results.key?(object_id)
54
+ end
55
+
56
+ def any_to_load?
57
+ lazy_values.any?
58
+ end
59
+
60
+ def lazy_ids
61
+ @lazy[:ids]
62
+ end
63
+
64
+ def lazy_values
65
+ @lazy[:values_to_load]
66
+ end
67
+
68
+ def lazy_results
69
+ @lazy[:results]
70
+ end
71
+ end
72
+
7
73
  class ActiveRecordRelation
8
74
  def initialize(type, association, scope: nil)
9
75
  @object_class = type.object.class
@@ -19,8 +85,7 @@ module GraphqlLazyLoad
19
85
  @lazy = {
20
86
  objects_to_load: Set.new,
21
87
  ids: Set.new,
22
- results: {},
23
- scope: scope
88
+ results: {}
24
89
  }
25
90
  type.context[context_key].push(@lazy)
26
91
  end
@@ -52,7 +117,7 @@ module GraphqlLazyLoad
52
117
  end
53
118
 
54
119
  def already_loaded_or_queued?
55
- lazy_objects.include?(object_id)
120
+ lazy_ids.include?(object_id)
56
121
  end
57
122
 
58
123
  def already_loaded?
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql_lazy_load
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathon Gardner
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-08-31 00:00:00.000000000 Z
11
+ date: 2019-09-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -140,6 +140,7 @@ files:
140
140
  - Rakefile
141
141
  - bin/console
142
142
  - bin/setup
143
+ - gems/graphql_lazy_load-0.1.0.gem
143
144
  - graphql_lazy_load.gemspec
144
145
  - lib/graphql_lazy_load.rb
145
146
  - lib/graphql_lazy_load/version.rb