graphql-relay 0.2.0 → 0.3.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
  SHA1:
3
- metadata.gz: 6284053a13755747eea7b324a8c95c6acac7a2ce
4
- data.tar.gz: 12fd8d7abd63b48100a90212fc2386762129fdd9
3
+ metadata.gz: 4a86533b3e1bc7030fe0016ee41e9492e25cc597
4
+ data.tar.gz: d80d3d7006d8f37a7f208b49c2fd3dc5f78081c2
5
5
  SHA512:
6
- metadata.gz: 0df6bf1e5c4f2ac0d10e8f1328159fab1c802e969c26c3c274c3e1075b525aca788b21f24cb8cd1a0ae636838143d00d9c4e83441b94735431ae08c00c288165
7
- data.tar.gz: 4a9e932f728d71c8763c072b4d6e419b1d37d5da5607fe68deb7bf87a4c1850ae19ba1328d2a0483accedfcea35ab4e4820d8d76202891363e85ed46cbae3121
6
+ metadata.gz: 9c0f624fba3f5e81724bdf73459cf72747f8f6f8ca9a569e15b99cd4391a72d25d098ec6f1c3932ce794d4d0db33b1ea2ec0e2292d11e7b79070989de2a0da09
7
+ data.tar.gz: 04e49ec547a0930ac9b26a54b5a7107dd5a61fe1f758449915c58b4ea7f4c2bbc4917a49e77fff80f0497a478b83624e1b97461dffc21694fec20d8fa6f55e03
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # graphql-relay
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/graphql-relay.svg)](http://badge.fury.io/rb/graphql-relay)
4
+ [![Build Status](https://travis-ci.org/rmosolgo/graphql-relay-ruby.svg?branch=master)](https://travis-ci.org/rmosolgo/graphql-relay-ruby)
4
5
  [![Code Climate](https://codeclimate.com/github/rmosolgo/graphql-relay-ruby/badges/gpa.svg)](https://codeclimate.com/github/rmosolgo/graphql-relay-ruby)
5
6
  [![Test Coverage](https://codeclimate.com/github/rmosolgo/graphql-relay-ruby/badges/coverage.svg)](https://codeclimate.com/github/rmosolgo/graphql-relay-ruby/coverage)
6
7
 
@@ -24,24 +25,19 @@ Global Ids provide refetching & global identification for Relay.
24
25
 
25
26
  You should implement an object that responds to `#object_from_id(global_id)` & `#type_from_object(object)`, then pass it to `GraphQL::Relay::Node.create(implementation)`. [Example](https://github.com/rmosolgo/graphql-relay-ruby/blob/120b750cf86f1eb5c9997b588f022b2ef3a0012c/spec/support/star_wars_schema.rb#L4-L15)
26
27
 
27
- Then, you can add global id fields to your types with `GraphQL::Relay::GlobalIdField.new(type_name)`. [Example](https://github.com/rmosolgo/graphql-relay-ruby/blob/120b750cf86f1eb5c9997b588f022b2ef3a0012c/spec/support/star_wars_schema.rb#L22)
28
+ Then, you can add global id fields to your types with `global_id_field` definition helper.
29
+ [Example](https://github.com/rmosolgo/graphql-relay-ruby/blob/master/spec/support/star_wars_schema.rb#L29)
28
30
 
29
31
  ### Connections
30
32
 
31
- Connections will provide arguments, pagination and `pageInfo` for `Array`s or `ActiveRecord::Relation`s.
32
-
33
- To create a connection, you should:
34
- - create a connection type; then
35
- - implement the field to return objects
36
-
37
- To create a connection type, use either `GraphQL::Relay::ArrayConnection.create_type(base_type)` or for an `ActiveRecord::Relation`, use `GraphQL::Relay::RelationConnection.create_type(base_type)`. [Array example](https://github.com/rmosolgo/graphql-relay-ruby/blob/120b750cf86f1eb5c9997b588f022b2ef3a0012c/spec/support/star_wars_schema.rb#L27), [Relation example](https://github.com/rmosolgo/graphql-relay-ruby/blob/120b750cf86f1eb5c9997b588f022b2ef3a0012c/spec/support/star_wars_schema.rb#L39)
33
+ Connections will provide arguments, pagination and `pageInfo` for `Array`s or `ActiveRecord::Relation`s. You can use the `connection` definition helper.
38
34
 
39
35
  Then, implement the field. It's different than a normal field:
40
36
  - use the `connection` helper to define it, instead of `field`
41
- - use the newly-created connection type as the field's return type
37
+ - Call `#connection_type` on an `ObjectType` for the field's return type (eg, `ShipType.connection_type`)
42
38
  - implement `resolve` to return an Array or an ActiveRecord::Relation, depending on the connection type.
43
39
 
44
- [Example](https://github.com/rmosolgo/graphql-relay-ruby/blob/120b750cf86f1eb5c9997b588f022b2ef3a0012c/spec/support/star_wars_schema.rb#L48-L61)
40
+ [Example 1](https://github.com/rmosolgo/graphql-relay-ruby/blob/master/spec/support/star_wars_schema.rb#L39-L51), [Example 2](https://github.com/rmosolgo/graphql-relay-ruby/blob/master/spec/support/star_wars_schema.rb#L52-L58)
45
41
 
46
42
  ### Mutations
47
43
 
@@ -69,7 +65,7 @@ Examples:
69
65
 
70
66
  ## Todo
71
67
 
72
- - [ ] pluralIdentifyingRootField
68
+ - [ ] Fix `Node.create` -- make it return one object which exposes useful info
73
69
 
74
70
  ## More Resources
75
71
 
@@ -3,14 +3,39 @@ module GraphQL
3
3
  module DefinedByConfig
4
4
  class DefinitionConfig
5
5
  # Wraps a field definition with a ConnectionField
6
+ # - applies default fields
7
+ # - wraps the resolve proc to make a connection
8
+ #
6
9
  def connection(name, type = nil, desc = nil, property: nil, &block)
7
- underlying_field = field(name, type, desc, property: property, &block)
8
- connection_field = GraphQL::Relay::ConnectionField.create(underlying_field)
10
+ # Wrap the given block to define the default args
11
+ definition_block = -> (config) {
12
+ argument :first, types.Int
13
+ argument :after, types.String
14
+ argument :last, types.Int
15
+ argument :before, types.String
16
+ argument :order, types.String
17
+ self.instance_eval(&block)
18
+ }
19
+ connection_field = field(name, type, desc, property: property, &definition_block)
20
+ # Wrap the defined resolve proc
21
+ # TODO: make a public API on GraphQL::Field to expose this proc
22
+ original_resolve = connection_field.instance_variable_get(:@resolve_proc)
23
+ connection_resolve = -> (obj, args, ctx) {
24
+ items = original_resolve.call(obj, args, ctx)
25
+ connection_class = GraphQL::Relay::BaseConnection.connection_for_items(items)
26
+ connection_class.new(items, args)
27
+ }
28
+ connection_field.resolve = connection_resolve
9
29
  fields[name.to_s] = connection_field
10
30
  end
11
31
 
12
32
  alias :return_field :field
13
33
  alias :return_fields :fields
34
+
35
+ def global_id_field(field_name)
36
+ name || raise("You must define the type's name before creating a GlobalIdField")
37
+ field(name, field: GraphQL::Relay::GlobalIdField.new(name))
38
+ end
14
39
  end
15
40
  end
16
41
  end
@@ -0,0 +1,7 @@
1
+ module GraphQL
2
+ class ObjectType
3
+ def connection_type
4
+ @connection_type ||= GraphQL::Relay::BaseConnection.create_type(self)
5
+ end
6
+ end
7
+ end
data/lib/graphql/relay.rb CHANGED
@@ -1,12 +1,13 @@
1
1
  require 'base64'
2
+ # MONKEY PATCHES 😬
2
3
  require 'graphql/definition_helpers/defined_by_config/definition_config'
4
+ require_relative './object_type.rb'
5
+
3
6
  require 'graphql/relay/node'
4
7
  require 'graphql/relay/page_info'
5
8
  require 'graphql/relay/edge'
6
- require 'graphql/relay/connection_type'
7
9
  require 'graphql/relay/base_connection'
8
10
  require 'graphql/relay/array_connection'
9
11
  require 'graphql/relay/relation_connection'
10
12
  require 'graphql/relay/global_id_field'
11
- require 'graphql/relay/connection_field'
12
13
  require 'graphql/relay/mutation'
@@ -44,5 +44,6 @@ module GraphQL
44
44
  index.to_i
45
45
  end
46
46
  end
47
+ BaseConnection.register_connection_implementation(Array, ArrayConnection)
47
48
  end
48
49
  end
@@ -13,21 +13,41 @@ module GraphQL
13
13
  # Just to encode data in the cursor, use something that won't conflict
14
14
  CURSOR_SEPARATOR = "---"
15
15
 
16
+ # Map of collection classes -> connection_classes
17
+ # eg Array -> ArrayConnection
18
+ CONNECTION_IMPLEMENTATIONS = {}
19
+
16
20
  # Create a connection which exposes edges of this type
17
21
  def self.create_type(wrapped_type)
18
22
  edge_type = Edge.create_type(wrapped_type)
19
23
 
20
- connection_type = ConnectionType.define do
24
+ connection_type = ObjectType.define do
21
25
  name("#{wrapped_type.name}Connection")
22
26
  field :edges, types[edge_type]
23
27
  field :pageInfo, PageInfo, property: :page_info
24
28
  end
25
29
 
26
- connection_type.connection_class = self
27
-
28
30
  connection_type
29
31
  end
30
32
 
33
+ # @return [subclass of BaseConnection] a connection wrapping `items`
34
+ def self.connection_for_items(items)
35
+ implementation = CONNECTION_IMPLEMENTATIONS.find do |items_class, connection_class|
36
+ items.is_a?(items_class)
37
+ end
38
+ if implementation.nil?
39
+ raise("No connection implementation to wrap #{items.class}")
40
+ else
41
+ implementation[1]
42
+ end
43
+ end
44
+
45
+ # Add `connection_class` as the connection wrapper for `items_class`
46
+ # eg, `RelationConnection` is the implementation for `AR::Relation`
47
+ def self.register_connection_implementation(items_class, connection_class)
48
+ CONNECTION_IMPLEMENTATIONS[items_class] = connection_class
49
+ end
50
+
31
51
  attr_reader :object, :arguments
32
52
 
33
53
  def initialize(object, arguments)
@@ -71,5 +71,8 @@ module GraphQL
71
71
  ["#{name} #{direction_marker} ?", value]
72
72
  end
73
73
  end
74
+ if defined?(ActiveRecord)
75
+ BaseConnection.register_connection_implementation(ActiveRecord::Relation, RelationConnection)
76
+ end
74
77
  end
75
78
  end
@@ -1,5 +1,5 @@
1
1
  module GraphQL
2
2
  module Relay
3
- VERSION = '0.2.0'
3
+ VERSION = '0.3.0'
4
4
  end
5
5
  end
@@ -5,11 +5,15 @@ describe GraphQL::Relay::ArrayConnection do
5
5
  ships = result["data"]["rebels"]["ships"]["edges"]
6
6
  names = ships.map { |e| e["node"]["name"] }
7
7
  end
8
+
9
+ def get_last_cursor(result)
10
+ result["data"]["rebels"]["ships"]["edges"].last["cursor"]
11
+ end
8
12
  describe "results" do
9
13
  let(:query_string) {%|
10
- query getShips($first: Int, $after: String, $last: Int, $before: String, $order: String){
14
+ query getShips($first: Int, $after: String, $last: Int, $before: String, $order: String, $nameIncludes: String){
11
15
  rebels {
12
- ships(first: $first, after: $after, last: $last, before: $before, order: $order) {
16
+ ships(first: $first, after: $after, last: $last, before: $before, order: $order, nameIncludes: $nameIncludes) {
13
17
  edges {
14
18
  cursor
15
19
  node {
@@ -47,7 +51,7 @@ describe GraphQL::Relay::ArrayConnection do
47
51
  assert_equal(["X-Wing", "Y-Wing", "A-Wing"], get_names(result))
48
52
 
49
53
  # After the last result, find the next 2:
50
- last_cursor = result["data"]["rebels"]["ships"]["edges"].last["cursor"]
54
+ last_cursor = get_last_cursor(result)
51
55
 
52
56
  result = query(query_string, "after" => last_cursor, "first" => 2)
53
57
  assert_equal(["Millenium Falcon", "Home One"], get_names(result))
@@ -71,5 +75,16 @@ describe GraphQL::Relay::ArrayConnection do
71
75
  result = query(query_string, "first" => 2, "order" => "-name")
72
76
  assert_equal(["Y-Wing", "X-Wing"], get_names(result))
73
77
  end
78
+
79
+ it 'applies custom arguments' do
80
+ result = query(query_string, "nameIncludes" => "Wing", "first" => 2)
81
+ names = get_names(result)
82
+ assert_equal(2, names.length)
83
+
84
+ after = get_last_cursor(result)
85
+ result = query(query_string, "nameIncludes" => "Wing", "after" => after)
86
+ names = get_names(result)
87
+ assert_equal(1, names.length)
88
+ end
74
89
  end
75
90
  end
@@ -15,18 +15,22 @@ describe GraphQL::Relay::RelationConnection do
15
15
  query getShips($first: Int, $after: String, $last: Int, $before: String, $order: String){
16
16
  empire {
17
17
  bases(first: $first, after: $after, last: $last, before: $before, order: $order) {
18
- edges {
19
- cursor
20
- node {
21
- name
22
- }
23
- }
18
+ ... basesConnection
24
19
  pageInfo {
25
20
  hasNextPage
26
21
  }
27
22
  }
28
23
  }
29
24
  }
25
+
26
+ fragment basesConnection on BaseConnection {
27
+ edges {
28
+ cursor
29
+ node {
30
+ name
31
+ }
32
+ }
33
+ }
30
34
  |}
31
35
  it 'limits the result' do
32
36
  result = query(query_string, "first" => 2)
@@ -23,36 +23,33 @@ Ship = GraphQL::ObjectType.define do
23
23
  field :name, types.String
24
24
  end
25
25
 
26
- # Define a connection which will wrap an array:
27
- ShipConnection = GraphQL::Relay::ArrayConnection.create_type(Ship)
28
-
29
-
30
26
  BaseType = GraphQL::ObjectType.define do
31
27
  name "Base"
32
28
  interfaces [NodeInterface]
33
- field :id, field: GraphQL::Relay::GlobalIdField.new("Base")
29
+ global_id_field :id
34
30
  field :name, types.String
35
31
  field :planet, types.String
36
32
  end
37
33
 
38
- # Define a connection which will wrap an ActiveRecord::Relation:
39
- BaseConnection = GraphQL::Relay::RelationConnection.create_type(BaseType)
40
-
41
-
42
-
43
34
  Faction = GraphQL::ObjectType.define do
44
35
  name "Faction"
45
36
  interfaces [NodeInterface]
46
37
  field :id, field: GraphQL::Relay::GlobalIdField.new("Faction")
47
38
  field :name, types.String
48
- connection :ships, ShipConnection do
39
+ connection :ships, Ship.connection_type do
49
40
  # Resolve field should return an Array, the Connection
50
41
  # will do the rest!
51
42
  resolve -> (obj, args, ctx) {
52
- obj.ships.map {|ship_id| STAR_WARS_DATA["Ship"][ship_id] }
43
+ all_ships = obj.ships.map {|ship_id| STAR_WARS_DATA["Ship"][ship_id] }
44
+ if args[:nameIncludes]
45
+ all_ships = all_ships.select { |ship| ship.name.include?(args[:nameIncludes])}
46
+ end
47
+ all_ships
53
48
  }
49
+ # You can define arguments here and use them in the connection
50
+ argument :nameIncludes, types.String
54
51
  end
55
- connection :bases, BaseConnection do
52
+ connection :bases, BaseType.connection_type do
56
53
  # Resolve field should return an Array, the Connection
57
54
  # will do the rest!
58
55
  resolve -> (obj, args, ctx) {
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql-relay
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-24 00:00:00.000000000 Z
11
+ date: 2015-08-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: graphql
@@ -188,11 +188,10 @@ extra_rdoc_files: []
188
188
  files:
189
189
  - README.md
190
190
  - lib/graphql/definition_helpers/defined_by_config/definition_config.rb
191
+ - lib/graphql/object_type.rb
191
192
  - lib/graphql/relay.rb
192
193
  - lib/graphql/relay/array_connection.rb
193
194
  - lib/graphql/relay/base_connection.rb
194
- - lib/graphql/relay/connection_field.rb
195
- - lib/graphql/relay/connection_type.rb
196
195
  - lib/graphql/relay/edge.rb
197
196
  - lib/graphql/relay/global_id_field.rb
198
197
  - lib/graphql/relay/mutation.rb
@@ -1,23 +0,0 @@
1
- module GraphQL
2
- module Relay
3
- # The best way to make these is with the connection helper,
4
- # @see {GraphQL::DefinitionHelpers::DefinedByConfig::DefinitionConfig}
5
- class ConnectionField
6
- def self.create(underlying_field)
7
- field = GraphQL::Field.define do
8
- argument :first, types.Int
9
- argument :after, types.String
10
- argument :last, types.Int
11
- argument :before, types.String
12
- argument :order, types.String
13
-
14
- type(-> { underlying_field.type })
15
- resolve -> (obj, args, ctx) {
16
- items = underlying_field.resolve(obj, args, ctx)
17
- underlying_field.type.connection_class.new(items, args)
18
- }
19
- end
20
- end
21
- end
22
- end
23
- end
@@ -1,10 +0,0 @@
1
- module GraphQL
2
- module Relay
3
- # An ObjectType which also stores its {#connection_class}
4
- class ConnectionType < GraphQL::ObjectType
5
- defined_by_config :name, :fields, :interfaces
6
- # @return [Class] A subclass of {BaseConnection}
7
- attr_accessor :connection_class
8
- end
9
- end
10
- end