search_object_graphql 0.1 → 0.2

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 (50) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +12 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +3 -2
  5. data/CHANGELOG.md +5 -0
  6. data/Gemfile +2 -0
  7. data/README.md +38 -29
  8. data/Rakefile +3 -1
  9. data/example/.ruby-version +1 -1
  10. data/example/Gemfile +3 -1
  11. data/example/README.md +3 -3
  12. data/example/Rakefile +2 -0
  13. data/example/app/controllers/application_controller.rb +2 -0
  14. data/example/app/controllers/graphql_controller.rb +22 -18
  15. data/example/app/graphql/mutations/.keep +0 -0
  16. data/example/app/graphql/resolvers/base_search_resolver.rb +2 -0
  17. data/example/app/graphql/resolvers/category_search.rb +5 -3
  18. data/example/app/graphql/resolvers/post_search.rb +6 -4
  19. data/example/app/graphql/schema.rb +3 -1
  20. data/example/app/graphql/types/base_enum.rb +6 -0
  21. data/example/app/graphql/types/base_input_object.rb +6 -0
  22. data/example/app/graphql/types/base_interface.rb +7 -0
  23. data/example/app/graphql/types/base_object.rb +6 -0
  24. data/example/app/graphql/types/base_scalar.rb +6 -0
  25. data/example/app/graphql/types/base_union.rb +6 -0
  26. data/example/app/graphql/types/category_type.rb +7 -6
  27. data/example/app/graphql/types/date_time_type.rb +3 -4
  28. data/example/app/graphql/types/mutation_type.rb +6 -0
  29. data/example/app/graphql/types/post_type.rb +13 -11
  30. data/example/app/graphql/types/query_type.rb +6 -5
  31. data/example/app/models/application_record.rb +2 -0
  32. data/example/app/models/category.rb +2 -0
  33. data/example/app/models/post.rb +2 -0
  34. data/example/bin/bundle +3 -1
  35. data/example/bin/rails +2 -0
  36. data/example/bin/rake +2 -0
  37. data/example/bin/setup +4 -2
  38. data/example/bin/update +4 -2
  39. data/example/config.ru +2 -0
  40. data/example/config/application.rb +1 -1
  41. data/example/db/migrate/20170507175133_create_demo_tables.rb +1 -1
  42. data/example/db/schema.rb +1 -1
  43. data/example/public/favicon.ico +0 -0
  44. data/lib/search_object/plugin/graphql.rb +89 -32
  45. data/lib/search_object/plugin/graphql/version.rb +3 -1
  46. data/search_object_graphql.gemspec +9 -9
  47. data/spec/search_object/plugin/graphql_spec.rb +88 -19
  48. data/spec/spec_helper.rb +2 -0
  49. data/spec/spec_helper_active_record.rb +2 -0
  50. metadata +30 -34
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 20d420d2ee1dd08bdfe48da2c5e1e3ba0a3a8233
4
- data.tar.gz: 57a5009d415d6fad948001d18d428676dbd1471c
2
+ SHA256:
3
+ metadata.gz: 3f77197a83b86e6ec05a3cb7f0f814bcd8e2d9d5590319b995657eccc64700f3
4
+ data.tar.gz: f31f8c4374c23da84bcccc42b6fc8307e08661cab1c67bed324aea90c2780faf
5
5
  SHA512:
6
- metadata.gz: d25a679fad52b5605c5720a774f2bee6d273abc369dbfd90cc48a75793d7fd3cc6359092406d178aecd0ae97fdb6e2f938f891b764b2ebfb26c47bf99fc63605
7
- data.tar.gz: f7d5f5e368c50e3bc274b8d665c83ca975f1093e55a7059c906f17e93a40388c097d51ce7288b99394b3d8268913bfe0ea1ef1d2a611bedaa0e7df9380160f5f
6
+ metadata.gz: 223b83055eaa7e7630694aee66c69abaa01d28a00f091535613b26c7d2f037370f6953ebcaba9d534315fcc381d0aecfbda06b0e0b25b9a0e015b01a233f29c8
7
+ data.tar.gz: 24a16c64a585f012bf10a10d0deefe724fec63338b7d901571595ccd92c7f8643373391f63165c2c30bb52d23a7d0e258f936dc7039ff06840140d303f9c5549
@@ -26,10 +26,22 @@ Style/EachWithObject:
26
26
  Style/CollectionMethods:
27
27
  Enabled: false
28
28
 
29
+ # Disables "Avoid the use of double negation (!!)."
30
+ Style/DoubleNegation:
31
+ Enabled: false
32
+
29
33
  # Disables "Block has too many lines."
30
34
  Metrics/BlockLength:
31
35
  Enabled: false
32
36
 
37
+ # Disables "Assignment Branch Condition size for field_options is too high."
38
+ Metrics/AbcSize:
39
+ Enabled: false
40
+
41
+ # Disables "Method has too many line."
42
+ Metrics/MethodLength:
43
+ Enabled: false
44
+
33
45
  # Disables "Example has too many lines."
34
46
  RSpec/ExampleLength:
35
47
  Enabled: false
@@ -0,0 +1 @@
1
+ 2.5.1
@@ -1,7 +1,8 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.2
4
- - 2.3.0
3
+ - 2.4.1
4
+ - 2.5.2
5
+ - 2.6.0
5
6
  script:
6
7
  - bundle exec rubocop
7
8
  - bundle exec rspec spec
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## Version 0.2
4
+
5
+ * Added support for GraphQL::Schema::Resolver (@rstankov)
6
+ * Added support for GraphQL 1.8 class API (@rstankov)
7
+
3
8
  ## Version 0.1
4
9
 
5
10
  * Initial release (@rstankov)
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in search_object.gemspec
data/README.md CHANGED
@@ -1,3 +1,4 @@
1
+ [![Gem Version](https://badge.fury.io/rb/search_object_graphql.svg)](http://badge.fury.io/rb/search_object_graphql)
1
2
  [![Code Climate](https://codeclimate.com/github/RStankov/SearchObjectGraphQL.svg)](https://codeclimate.com/github/RStankov/SearchObjectGraphQL)
2
3
  [![Build Status](https://secure.travis-ci.org/RStankov/SearchObjectGraphQL.svg)](http://travis-ci.org/RStankov/SearchObjectGraphQL)
3
4
  [![Code coverage](https://coveralls.io/repos/RStankov/SearchObjectGraphQL/badge.svg?branch=master#2)](https://coveralls.io/r/RStankov/SearchObjectGraphQL)
@@ -19,13 +20,15 @@
19
20
  * [Accessing Parent Object](#accessing-parent-object)
20
21
  * [Enums Support](#enums-support)
21
22
  * [Relay Support](#relay-support)
23
+ * [Contributing](#contributing)
24
+ * [License](#license)
22
25
 
23
26
  ## Installation
24
27
 
25
28
  Add this line to your application's Gemfile:
26
29
 
27
30
  ```ruby
28
- gem 'search_object'
31
+ gem 'search_object_graphql'
29
32
  ```
30
33
 
31
34
  And then execute:
@@ -34,7 +37,7 @@ And then execute:
34
37
 
35
38
  Or install it yourself as:
36
39
 
37
- $ gem install search_object
40
+ $ gem install search_object_graphql
38
41
 
39
42
  ## Dependencies
40
43
 
@@ -49,19 +52,19 @@ Just include the ```SearchObject.module``` and define your search options and th
49
52
  class PostResolver
50
53
  include SearchObject.module(:graphql)
51
54
 
52
- type PostType
55
+ type [PostType], null: false
53
56
 
54
57
  scope { Post.all }
55
58
 
56
- option(:name, type: types.String) { |scope, value| scope.where name: value }
57
- option(:published, type: types.Boolean) { |scope, value| value ? scope.published : scope.unpublished }
59
+ option(:name, type: String) { |scope, value| scope.where name: value }
60
+ option(:published, type: Boolean) { |scope, value| value ? scope.published : scope.unpublished }
58
61
  end
59
62
  ```
60
63
 
61
- Then you can just use `PostResolver` as [GraphQL::Function](https://rmosolgo.github.io/graphql-ruby/schema/code_reuse#functions):
64
+ Then you can just use `PostResolver` as [GraphQL::Schema::Resolver](https://graphql-ruby.org/fields/resolvers.html):
62
65
 
63
66
  ```ruby
64
- field :posts, function: PostResolver
67
+ field :posts, resolver: PostResolver
65
68
  ```
66
69
 
67
70
  Options are exposed as arguments in the GraphQL query:
@@ -78,26 +81,6 @@ You can find example of most important features and plugins - [here](https://git
78
81
 
79
82
  ## Features
80
83
 
81
- ### Custom Types
82
-
83
- Custom types can be define inside the search object:
84
-
85
- ```ruby
86
- class PostResolver
87
- include SearchObject.module(:graphql)
88
-
89
- type do
90
- name 'Custom Type'
91
-
92
- field :id, !types.ID
93
- field :title, !types.String
94
- field :body, !types.String
95
- end
96
-
97
- # ...
98
- end
99
- ```
100
-
101
84
  ### Documentation
102
85
 
103
86
  Search object itself can be documented, as well as its options:
@@ -174,10 +157,36 @@ end
174
157
 
175
158
  ### Relay Support
176
159
 
177
- Search objects can be used as [Relay Connections](https://rmosolgo.github.io/graphql-ruby/relay/connections):
160
+ Search objects can be used as [Relay Connections](https://graphql-ruby.org/relay/connections.html):
178
161
 
179
162
  ```ruby
180
- connection :posts, Types::PostType.connection_type, function: Resolvers::PostSearch
163
+ class PostResolver
164
+ include SearchObject.module(:graphql)
165
+
166
+ type PostType.connection_type, null: false
167
+
168
+ # ...
169
+ end
170
+ ```
171
+
172
+ ```ruby
173
+ field :posts, resolver: PostResolver
174
+ ```
175
+
176
+ ### Legacy Function Support
177
+
178
+ ```ruby
179
+ class PostResolver
180
+ include SearchObject.module(:graphql)
181
+
182
+ type [PostType], null: false
183
+
184
+ # ...
185
+ end
186
+ ```
187
+
188
+ ```ruby
189
+ field :posts, function: PostResolver
181
190
  ```
182
191
 
183
192
  ## Contributing
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/gem_tasks'
2
4
  require 'rspec/core/rake_task'
3
5
  require 'rubocop/rake_task'
@@ -5,4 +7,4 @@ require 'rubocop/rake_task'
5
7
  RSpec::Core::RakeTask.new(:spec)
6
8
  RuboCop::RakeTask.new(:rubocop)
7
9
 
8
- task default: [:rubocop, :spec]
10
+ task default: %i[rubocop spec]
@@ -1 +1 @@
1
- 2.3.1
1
+ 2.5.1
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  git_source(:github) do |repo_name|
@@ -5,7 +7,7 @@ git_source(:github) do |repo_name|
5
7
  "https://github.com/#{repo_name}.git"
6
8
  end
7
9
 
8
- gem 'rails', '~> 5.1.0'
10
+ gem 'rails', '~> 5.2.0'
9
11
 
10
12
  gem 'graphql'
11
13
  gem 'puma', '~> 3.7'
@@ -16,9 +16,9 @@ This is example application showing, one of the possible usages of ```SearchObje
16
16
  ```
17
17
  gem install bundler
18
18
  bundle install
19
- rake db:create
20
- rake db:migrate
21
- rake db:seed
19
+ rails db:create
20
+ rails db:migrate
21
+ rails db:seed
22
22
 
23
23
  rails server
24
24
  ```
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Add your own tasks in files placed in lib/tasks ending in .rake,
2
4
  # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
5
 
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ApplicationController < ActionController::API
2
4
  end
@@ -1,29 +1,33 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class GraphqlController < ApplicationController
2
4
  def execute
3
- render json: Schema.execute(query, variables: variables, context: context)
5
+ result = Schema.execute(params[:query],
6
+ variables: ensure_hash(params[:variables]),
7
+ context: {},
8
+ operation_name: params[:operationName])
9
+ render json: result
10
+ rescue StandardError => error
11
+ raise error unless Rails.env.development?
12
+
13
+ handle_error_in_development error
4
14
  end
5
15
 
6
16
  private
7
17
 
8
- def query
9
- params[:query]
10
- end
11
-
12
- def variables
13
- ensure_hash(params[:variables])
18
+ def ensure_hash(ambiguous_param)
19
+ case ambiguous_param
20
+ when String then ambiguous_param.present? ? ensure_hash(JSON.parse(ambiguous_param)) : {}
21
+ when Hash, ActionController::Parameters then ambiguous_param
22
+ when nil then {}
23
+ else raise ArgumentError, "Unexpected parameter: #{ambiguous_param}"
24
+ end
14
25
  end
15
26
 
16
- def context
17
- {}
18
- end
27
+ def handle_error_in_development(error)
28
+ logger.error error.message
29
+ logger.error error.backtrace.join("\n")
19
30
 
20
- # Handle form data, JSON body, or a blank value
21
- def ensure_hash(params)
22
- case params
23
- when String then params.present? ? ensure_hash(JSON.parse(params)) : {}
24
- when Hash, ActionController::Parameters then params
25
- when nil then {}
26
- else raise ArgumentError, "Unexpected parameter: #{params}"
27
- end
31
+ render json: { error: { message: error.message, backtrace: error.backtrace }, data: {} }, status: 500
28
32
  end
29
33
  end
File without changes
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Resolvers
2
4
  class BaseSearchResolver
3
5
  include SearchObject.module(:graphql)
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Resolvers
2
4
  class CategorySearch < Resolvers::BaseSearchResolver
3
- type Types::CategoryType
5
+ type Types::CategoryType.connection_type
4
6
  description 'Lists categories'
5
7
 
6
- OrderEnum = GraphQL::EnumType.define do
7
- name 'CategoryOrder'
8
+ class OrderEnum < Types::BaseEnum
9
+ graphql_name 'CategoryOrder'
8
10
 
9
11
  value 'RECENT'
10
12
  value 'NAME'
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Resolvers
2
4
  class PostSearch < Resolvers::BaseSearchResolver
3
- type Types::PostType
5
+ type Types::PostType.connection_type
4
6
  description 'Lists posts'
5
7
 
6
- OrderEnum = GraphQL::EnumType.define do
7
- name 'PostOrder'
8
+ class OrderEnum < Types::BaseEnum
9
+ graphql_name 'PostOrder'
8
10
 
9
11
  value 'RECENT'
10
12
  value 'VIEWS'
@@ -51,7 +53,7 @@ module Resolvers
51
53
  end
52
54
 
53
55
  def apply_order_with_recent(scope)
54
- scope.order 'published_at IS NOT NULL, published_at DESC'
56
+ scope.order Arel.sql('published_at IS NOT NULL'), published_at: :desc
55
57
  end
56
58
 
57
59
  def apply_order_with_views(scope)
@@ -1,3 +1,5 @@
1
- Schema = GraphQL::Schema.define do
1
+ # frozen_string_literal: true
2
+
3
+ class Schema < GraphQL::Schema
2
4
  query Types::QueryType
3
5
  end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Types
4
+ class BaseEnum < GraphQL::Schema::Enum
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Types
4
+ class BaseInputObject < GraphQL::Schema::InputObject
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Types
4
+ module BaseInterface
5
+ include GraphQL::Schema::Interface
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Types
4
+ class BaseObject < GraphQL::Schema::Object
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Types
4
+ class BaseScalar < GraphQL::Schema::Scalar
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Types
4
+ class BaseUnion < GraphQL::Schema::Union
5
+ end
6
+ end
@@ -1,8 +1,9 @@
1
- Types::CategoryType = GraphQL::ObjectType.define do
2
- name 'Category'
1
+ # frozen_string_literal: true
3
2
 
4
- field :id, !types.ID
5
- field :name, !types.String
6
-
7
- connection :posts, Types::PostType.connection_type, function: Resolvers::PostSearch
3
+ module Types
4
+ class CategoryType < BaseObject
5
+ field :id, ID, null: false
6
+ field :name, String, null: false
7
+ field :posts, function: Resolvers::PostSearch
8
+ end
8
9
  end
@@ -1,6 +1,5 @@
1
- Types::DateTimeType = GraphQL::ScalarType.define do
2
- name 'DateTime'
1
+ # frozen_string_literal: true
3
2
 
4
- coerce_input ->(value, _ctx) { Time.zone.parse(value) }
5
- coerce_result ->(value, _ctx) { value.utc.iso8601 }
3
+ module Types
4
+ DateTimeType = GraphQL::Types::ISO8601DateTime
6
5
  end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Types
4
+ class MutationType < Types::BaseObject
5
+ end
6
+ end
@@ -1,13 +1,15 @@
1
- Types::PostType = GraphQL::ObjectType.define do
2
- name 'Post'
1
+ # frozen_string_literal: true
3
2
 
4
- field :id, !types.ID
5
- field :title, !types.String
6
- field :body, !types.String
7
- field :category, !Types::CategoryType
8
- field :viewsCount, !types.Int, property: :views_count
9
- field :likesCount, !types.Int, property: :likes_count
10
- field :commentsCount, !types.Int, property: :comments_count
11
- field :isPublished, !types.Boolean, property: :published?
12
- field :publishedAt, !Types::DateTimeType, property: :published_at
3
+ module Types
4
+ class PostType < BaseObject
5
+ field :id, ID, null: false
6
+ field :title, String, null: false
7
+ field :body, String, null: false
8
+ field :category, CategoryType, null: false
9
+ field :views_count, Int, null: false
10
+ field :likes_count, Int, null: false
11
+ field :comments_count, Int, null: false
12
+ field :is_published, Boolean, null: false, method: :published?
13
+ field :published_at, DateTimeType, null: false
14
+ end
13
15
  end
@@ -1,7 +1,8 @@
1
- Types::QueryType = GraphQL::ObjectType.define do
2
- name 'Query'
1
+ # frozen_string_literal: true
3
2
 
4
- connection :categories, Types::CategoryType.connection_type, function: Resolvers::CategorySearch
5
-
6
- connection :posts, Types::PostType.connection_type, function: Resolvers::PostSearch
3
+ module Types
4
+ class QueryType < Types::BaseObject
5
+ field :categories, function: Resolvers::CategorySearch
6
+ field :posts, resolver: Resolvers::PostSearch
7
+ end
7
8
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ApplicationRecord < ActiveRecord::Base
2
4
  self.abstract_class = true
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Category < ApplicationRecord
2
4
  validates :name, presence: true, uniqueness: true
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Post < ApplicationRecord
2
4
  validates :title, presence: true, uniqueness: true
3
5
  validates :body, presence: true
@@ -1,3 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
2
+ # frozen_string_literal: true
3
+
4
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
3
5
  load Gem.bin_path('bundler', 'bundle')
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
2
4
  APP_PATH = File.expand_path('../config/application', __dir__)
3
5
  require_relative '../config/boot'
4
6
  require 'rails/commands'
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
2
4
  require_relative '../config/boot'
3
5
  require 'rake'
4
6
  Rake.application.run
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
2
4
  require 'pathname'
3
5
  require 'fileutils'
4
- include FileUtils
6
+ include FileUtils # rubocop:disable Style/MixinUsage
5
7
 
6
8
  # path to your application root.
7
- APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
9
+ APP_ROOT = Pathname.new File.expand_path('..', __dir__)
8
10
 
9
11
  def system!(*args)
10
12
  system(*args) || abort("\n== Command #{args} failed ==")
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
2
4
  require 'pathname'
3
5
  require 'fileutils'
4
- include FileUtils
6
+ include FileUtils # rubocop:disable Style/MixinUsage
5
7
 
6
8
  # path to your application root.
7
- APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
9
+ APP_ROOT = Pathname.new File.expand_path('..', __dir__)
8
10
 
9
11
  def system!(*args)
10
12
  system(*args) || abort("\n== Command #{args} failed ==")
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is used by Rack-based servers to start the application.
2
4
 
3
5
  require_relative 'config/environment'
@@ -3,7 +3,7 @@ require_relative 'boot'
3
3
  require "rails"
4
4
  # Pick the frameworks you want:
5
5
  require "active_model/railtie"
6
- # require "active_job/railtie"
6
+ require "active_job/railtie"
7
7
  require "active_record/railtie"
8
8
  require "action_controller/railtie"
9
9
  # require "action_mailer/railtie"
@@ -7,7 +7,7 @@ class CreateDemoTables < ActiveRecord::Migration[5.1]
7
7
  end
8
8
 
9
9
  create_table :posts do |t|
10
- t.references :category, foreign_key: true
10
+ t.references :category
11
11
  t.string :title, null: false
12
12
  t.index :title, unique: true
13
13
  t.string :body, null: false
@@ -10,7 +10,7 @@
10
10
  #
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
- ActiveRecord::Schema.define(version: 20170507175133) do
13
+ ActiveRecord::Schema.define(version: 2017_05_07_175133) do
14
14
 
15
15
  create_table "categories", force: :cascade do |t|
16
16
  t.string "name", null: false
File without changes
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SearchObject
2
4
  module Plugin
3
5
  module Graphql
4
6
  def self.included(base)
5
7
  base.include SearchObject::Plugin::Enum
8
+ base.include ::GraphQL::Schema::Member::GraphQLTypeNames
6
9
  base.extend ClassMethods
7
10
  end
8
11
 
@@ -15,49 +18,117 @@ module SearchObject
15
18
  super filters: filters, scope: scope
16
19
  end
17
20
 
21
+ # NOTE(rstankov): GraphQL::Schema::Resolver interface
22
+ # Documentation - http://graphql-ruby.org/fields/resolvers.html#using-resolver
23
+ def resolve_with_support(args = {})
24
+ self.params = args.to_h
25
+ results
26
+ end
27
+
18
28
  module ClassMethods
29
+ KEYS = %i[type default description].freeze
19
30
  def option(name, options = {}, &block)
20
- argument = Helper.build_argument(name, options)
21
- arguments[argument.name] = argument
31
+ config[:arguments] ||= {}
32
+ config[:arguments][name.to_s] = KEYS.inject({}) do |acc, key|
33
+ acc[key] = options[key] if options.key?(key)
34
+ acc
35
+ end
22
36
 
23
- options[:enum] = argument.type.values.keys if argument.type.is_a? GraphQL::EnumType
37
+ type = options.fetch(:type) { raise MissingTypeDefinitionError, name }
38
+ options[:enum] = type.values.keys if type.respond_to?(:values)
24
39
 
25
40
  super(name, options, &block)
26
41
  end
27
42
 
28
- def types
29
- GraphQL::Define::TypeDefiner.instance
30
- end
31
-
32
- # NOTE(rstankov): GraphQL::Function interface
33
- # Documentation - https://rmosolgo.github.io/graphql-ruby/schema/code_reuse#functions
34
- def call(object, args, context)
35
- new(filters: args.to_h, object: object, context: context).results
36
- end
37
-
38
- def arguments
39
- config[:args] ||= {}
40
- end
41
-
42
- def type(value = :default, &block)
43
+ def type(value = :default, null: true, &block)
43
44
  return config[:type] if value == :default && !block_given?
45
+
44
46
  config[:type] = block_given? ? GraphQL::ObjectType.define(&block) : value
47
+ config[:null] = null
45
48
  end
46
49
 
47
50
  def complexity(value = :default)
48
51
  return config[:complexity] || 1 if value == :default
52
+
49
53
  config[:complexity] = value
50
54
  end
51
55
 
52
56
  def description(value = :default)
53
57
  return config[:description] if value == :default
58
+
54
59
  config[:description] = value
55
60
  end
56
61
 
57
62
  def deprecation_reason(value = :default)
58
63
  return config[:deprecation_reason] if value == :default
64
+
59
65
  config[:deprecation_reason] = value
60
66
  end
67
+
68
+ # NOTE(rstankov): GraphQL::Function interface (deprecated in favour of GraphQL::Schema::Resolver)
69
+ # Documentation - http://graphql-ruby.org/guides
70
+ def call(object, args, context)
71
+ new(filters: args.to_h, object: object, context: context).results
72
+ end
73
+
74
+ # NOTE(rstankov): Used for GraphQL::Function
75
+ def types
76
+ GraphQL::Define::TypeDefiner.instance
77
+ end
78
+
79
+ # NOTE(rstankov): Used for GraphQL::Function
80
+ def arguments
81
+ (config[:arguments] || {}).inject({}) do |acc, (name, options)|
82
+ argument = GraphQL::Argument.new
83
+ argument.name = name.to_s
84
+ argument.type = options.fetch(:type) { raise MissingTypeDefinitionError, name }
85
+ argument.default_value = options[:default] if options.key? :default
86
+ argument.description = options[:description] if options.key? :description
87
+
88
+ acc[name] = argument
89
+ acc
90
+ end
91
+ end
92
+
93
+ # NOTE(rstankov): Used for GraphQL::Schema::Resolver
94
+ def field_options
95
+ {
96
+ type: type,
97
+ description: description,
98
+ extras: [],
99
+ resolver_method: :resolve_with_support,
100
+ resolver_class: self,
101
+ deprecation_reason: deprecation_reason,
102
+ arguments: (config[:arguments] || {}).inject({}) do |acc, (name, options)|
103
+ acc[name] = ::GraphQL::Schema::Argument.new(
104
+ name: name.to_s,
105
+ type: options.fetch(:type) { raise MissingTypeDefinitionError, name },
106
+ description: options[:description],
107
+ required: !!options[:required],
108
+ default_value: options.fetch(:default) { ::GraphQL::Schema::Argument::NO_DEFAULT },
109
+ owner: self
110
+ )
111
+ acc
112
+ end,
113
+ null: !!config[:null],
114
+ complexity: complexity
115
+ }
116
+ end
117
+
118
+ # NOTE(rstankov): Used for GraphQL::Schema::Resolver
119
+ def visible?(_context)
120
+ true
121
+ end
122
+
123
+ # NOTE(rstankov): Used for GraphQL::Schema::Resolver
124
+ def accessible?(_context)
125
+ true
126
+ end
127
+
128
+ # NOTE(rstankov): Used for GraphQL::Schema::Resolver
129
+ def authorized?(_object, _context)
130
+ true
131
+ end
61
132
  end
62
133
 
63
134
  class MissingTypeDefinitionError < ArgumentError
@@ -65,20 +136,6 @@ module SearchObject
65
136
  super "GraphQL type has to passed as :type to '#{name}' option"
66
137
  end
67
138
  end
68
-
69
- # :api: private
70
- module Helper
71
- module_function
72
-
73
- def build_argument(name, options)
74
- argument = GraphQL::Argument.new
75
- argument.name = name.to_s
76
- argument.type = options.fetch(:type) { raise MissingTypeDefinitionError, name }
77
- argument.default_value = options[:default] if options.key? :default
78
- argument.description = options[:description] if options.key? :description
79
- argument
80
- end
81
- end
82
139
  end
83
140
  end
84
141
  end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SearchObject
2
4
  module Plugin
3
5
  module Graphql
4
- VERSION = '0.1'.freeze
6
+ VERSION = '0.2'
5
7
  end
6
8
  end
7
9
  end
@@ -1,5 +1,6 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'English'
5
6
  require 'search_object/plugin/graphql/version'
@@ -19,13 +20,12 @@ Gem::Specification.new do |spec|
19
20
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
21
  spec.require_paths = ['lib']
21
22
 
22
- spec.add_dependency 'search_object', '~> 1.2'
23
- spec.add_dependency 'graphql', '~> 1.5'
23
+ spec.add_dependency 'graphql', '~> 1.8'
24
+ spec.add_dependency 'search_object', '~> 1.2.2'
24
25
 
25
- spec.add_development_dependency 'bundler', '~> 1.13'
26
- spec.add_development_dependency 'rake'
27
- spec.add_development_dependency 'rspec', '~> 3.5'
28
- spec.add_development_dependency 'rubocop', '0.46.0'
29
- spec.add_development_dependency 'rubocop-rspec', '1.8.0'
30
26
  spec.add_development_dependency 'coveralls'
27
+ spec.add_development_dependency 'rake'
28
+ spec.add_development_dependency 'rspec', '~> 3.8'
29
+ spec.add_development_dependency 'rubocop', '0.62.0'
30
+ spec.add_development_dependency 'rubocop-rspec', '1.31.0'
31
31
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
  require 'graphql'
3
5
  require 'ostruct'
@@ -10,20 +12,18 @@ describe SearchObject::Plugin::Graphql do
10
12
  end
11
13
  end
12
14
 
13
- PostType = GraphQL::ObjectType.define do
14
- name 'Post'
15
-
16
- field :id, !types.ID
15
+ class PostType < GraphQL::Schema::Object
16
+ field :id, ID, null: false
17
17
  end
18
18
 
19
19
  def define_schema(&block)
20
- query_type = GraphQL::ObjectType.define do
21
- name 'Query'
20
+ query_type = Class.new(GraphQL::Schema::Object) do
21
+ graphql_name 'Query'
22
22
 
23
23
  instance_eval(&block)
24
24
  end
25
25
 
26
- GraphQL::Schema.define do
26
+ Class.new(GraphQL::Schema) do
27
27
  query query_type
28
28
 
29
29
  max_complexity 1000
@@ -45,20 +45,60 @@ describe SearchObject::Plugin::Graphql do
45
45
 
46
46
  define_schema do
47
47
  if search_object.type.nil?
48
- field :posts, types[PostType], function: search_object
48
+ field :posts, [PostType], resolver: search_object
49
49
  else
50
- field :posts, function: search_object
50
+ field :posts, resolver: search_object
51
51
  end
52
52
  end
53
53
  end
54
54
 
55
+ it 'can be used as GraphQL::Schema::Resolver' do
56
+ post_type = Class.new(GraphQL::Schema::Object) do
57
+ graphql_name 'Post'
58
+
59
+ field :id, GraphQL::Types::ID, null: false
60
+ end
61
+
62
+ search_object = define_search_class do
63
+ scope { [Post.new('1'), Post.new('2'), Post.new('3')] }
64
+
65
+ type [post_type]
66
+
67
+ option(:id, type: !types.ID) { |scope, value| scope.select { |p| p.id == value } }
68
+ end
69
+
70
+ schema = define_schema do
71
+ field :posts, resolver: search_object
72
+ end
73
+
74
+ result = schema.execute '{ posts(id: "2") { id } }'
75
+
76
+ expect(result).to eq(
77
+ 'data' => {
78
+ 'posts' => [Post.new('2').to_json]
79
+ }
80
+ )
81
+ end
82
+
55
83
  it 'can be used as GraphQL::Function' do
56
- schema = define_search_class_and_return_schema do
84
+ post_type = GraphQL::ObjectType.define do
85
+ name 'Post'
86
+
87
+ field :id, !types.ID
88
+ end
89
+
90
+ search_object = define_search_class do
57
91
  scope { [Post.new('1'), Post.new('2'), Post.new('3')] }
58
92
 
93
+ type types[post_type]
94
+
59
95
  option(:id, type: !types.ID) { |scope, value| scope.select { |p| p.id == value } }
60
96
  end
61
97
 
98
+ schema = define_schema do
99
+ field :posts, function: search_object
100
+ end
101
+
62
102
  result = schema.execute '{ posts(id: "2") { id } }'
63
103
 
64
104
  expect(result).to eq(
@@ -73,19 +113,19 @@ describe SearchObject::Plugin::Graphql do
73
113
  scope { object.posts }
74
114
  end
75
115
 
76
- parent_type = GraphQL::ObjectType.define do
77
- name 'ParentType'
116
+ parent_type = Class.new(GraphQL::Schema::Object) do
117
+ graphql_name 'Parent'
78
118
 
79
- field :posts, types[PostType], function: search_object
119
+ field :posts, [PostType], resolver: search_object
80
120
  end
81
121
 
82
122
  schema = define_schema do
83
- field :parent, parent_type do
84
- resolve ->(_obj, _args, _ctx) { OpenStruct.new posts: [Post.new('from_parent')] }
85
- end
123
+ field :parent, parent_type, null: false
86
124
  end
87
125
 
88
- result = schema.execute '{ parent { posts { id } } }'
126
+ root = OpenStruct.new(parent: OpenStruct.new(posts: [Post.new('from_parent')]))
127
+
128
+ result = schema.execute '{ parent { posts { id } } }', root_value: root
89
129
 
90
130
  expect(result).to eq(
91
131
  'data' => {
@@ -174,7 +214,7 @@ describe SearchObject::Plugin::Graphql do
174
214
 
175
215
  it 'can be marked as deprecated' do
176
216
  schema = define_search_class_and_return_schema do
177
- type types[PostType]
217
+ type [PostType]
178
218
  deprecation_reason 'Not needed any more'
179
219
  end
180
220
 
@@ -189,7 +229,7 @@ describe SearchObject::Plugin::Graphql do
189
229
  }
190
230
  QUERY
191
231
 
192
- expect(result).to eq(
232
+ expect(result.to_h).to eq(
193
233
  'data' => {
194
234
  '__type' => {
195
235
  'name' => 'Query',
@@ -200,6 +240,35 @@ describe SearchObject::Plugin::Graphql do
200
240
  end
201
241
 
202
242
  describe 'option' do
243
+ it 'converts GraphQL::Schema::Enum to SearchObject enum' do
244
+ schema = define_search_class_and_return_schema do
245
+ enum_type = Class.new(GraphQL::Schema::Enum) do
246
+ graphql_name 'PostOrder'
247
+
248
+ value 'PRICE'
249
+ value 'DATE'
250
+ end
251
+
252
+ option(:order, type: enum_type)
253
+
254
+ define_method(:apply_order_with_price) do |_scope|
255
+ [Post.new('price')]
256
+ end
257
+
258
+ define_method(:apply_order_with_date) do |_scope|
259
+ [Post.new('date')]
260
+ end
261
+ end
262
+
263
+ result = schema.execute '{ posts(order: PRICE) { id } }'
264
+
265
+ expect(result).to eq(
266
+ 'data' => {
267
+ 'posts' => [Post.new('price').to_json]
268
+ }
269
+ )
270
+ end
271
+
203
272
  it 'converts GraphQL::EnumType to SearchObject enum' do
204
273
  schema = define_search_class_and_return_schema do
205
274
  enum_type = GraphQL::EnumType.define do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/setup'
2
4
 
3
5
  if ENV['TRAVIS']
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'spec_helper'
2
4
  require 'active_record'
3
5
 
metadata CHANGED
@@ -1,57 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: search_object_graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.1'
4
+ version: '0.2'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Radoslav Stankov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-05-07 00:00:00.000000000 Z
11
+ date: 2019-06-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: search_object
14
+ name: graphql
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.2'
19
+ version: '1.8'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.2'
26
+ version: '1.8'
27
27
  - !ruby/object:Gem::Dependency
28
- name: graphql
28
+ name: search_object
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.5'
33
+ version: 1.2.2
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.5'
40
+ version: 1.2.2
41
41
  - !ruby/object:Gem::Dependency
42
- name: bundler
42
+ name: coveralls
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '1.13'
47
+ version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '1.13'
54
+ version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -72,56 +72,42 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '3.5'
75
+ version: '3.8'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '3.5'
82
+ version: '3.8'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rubocop
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - '='
88
88
  - !ruby/object:Gem::Version
89
- version: 0.46.0
89
+ version: 0.62.0
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - '='
95
95
  - !ruby/object:Gem::Version
96
- version: 0.46.0
96
+ version: 0.62.0
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: rubocop-rspec
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - '='
102
102
  - !ruby/object:Gem::Version
103
- version: 1.8.0
103
+ version: 1.31.0
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - '='
109
109
  - !ruby/object:Gem::Version
110
- version: 1.8.0
111
- - !ruby/object:Gem::Dependency
112
- name: coveralls
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - ">="
116
- - !ruby/object:Gem::Version
117
- version: '0'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - ">="
123
- - !ruby/object:Gem::Version
124
- version: '0'
110
+ version: 1.31.0
125
111
  description: Search Object plugin to working with GraphQL
126
112
  email:
127
113
  - rstankov@gmail.com
@@ -133,6 +119,7 @@ files:
133
119
  - ".projections.json"
134
120
  - ".rspec"
135
121
  - ".rubocop.yml"
122
+ - ".ruby-version"
136
123
  - ".travis.yml"
137
124
  - CHANGELOG.md
138
125
  - Gemfile
@@ -146,13 +133,21 @@ files:
146
133
  - example/Rakefile
147
134
  - example/app/controllers/application_controller.rb
148
135
  - example/app/controllers/graphql_controller.rb
136
+ - example/app/graphql/mutations/.keep
149
137
  - example/app/graphql/resolvers/base_search_resolver.rb
150
138
  - example/app/graphql/resolvers/category_search.rb
151
139
  - example/app/graphql/resolvers/post_search.rb
152
140
  - example/app/graphql/schema.rb
153
141
  - example/app/graphql/types/.keep
142
+ - example/app/graphql/types/base_enum.rb
143
+ - example/app/graphql/types/base_input_object.rb
144
+ - example/app/graphql/types/base_interface.rb
145
+ - example/app/graphql/types/base_object.rb
146
+ - example/app/graphql/types/base_scalar.rb
147
+ - example/app/graphql/types/base_union.rb
154
148
  - example/app/graphql/types/category_type.rb
155
149
  - example/app/graphql/types/date_time_type.rb
150
+ - example/app/graphql/types/mutation_type.rb
156
151
  - example/app/graphql/types/post_type.rb
157
152
  - example/app/graphql/types/query_type.rb
158
153
  - example/app/models/application_record.rb
@@ -181,6 +176,7 @@ files:
181
176
  - example/db/schema.rb
182
177
  - example/db/seeds.rb
183
178
  - example/log/.keep
179
+ - example/public/favicon.ico
184
180
  - example/public/robots.txt
185
181
  - example/vendor/.keep
186
182
  - lib/search_object/plugin/graphql.rb
@@ -209,7 +205,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
209
205
  version: '0'
210
206
  requirements: []
211
207
  rubyforge_project:
212
- rubygems_version: 2.4.5
208
+ rubygems_version: 2.7.6
213
209
  signing_key:
214
210
  specification_version: 4
215
211
  summary: Maps search objects to GraphQL resolvers