graphql_lazy_load 0.1.0 → 0.2.0

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