Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,22 @@ Style/EachWithObject:
Style/CollectionMethods:
Enabled: false

# Disables "Avoid the use of double negation (!!)."
Style/DoubleNegation:
Enabled: false

# Disables "Block has too many lines."
Metrics/BlockLength:
Enabled: false

# Disables "Assignment Branch Condition size for field_options is too high."
Metrics/AbcSize:
Enabled: false

# Disables "Method has too many line."
Metrics/MethodLength:
Enabled: false

# Disables "Example has too many lines."
RSpec/ExampleLength:
Enabled: false
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## Version 0.2

* Added support for GraphQL::Schema::Resolver (@rstankov)
* Added support for GraphQL 1.8 class API (@rstankov)

## Version 0.1

* Initial release (@rstankov)
60 changes: 33 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,19 @@ Just include the ```SearchObject.module``` and define your search options and th
class PostResolver
include SearchObject.module(:graphql)

type types[PostType]
type [PostType], null: false

scope { Post.all }

option(:name, type: types.String) { |scope, value| scope.where name: value }
option(:published, type: types.Boolean) { |scope, value| value ? scope.published : scope.unpublished }
option(:name, type: String) { |scope, value| scope.where name: value }
option(:published, type: Boolean) { |scope, value| value ? scope.published : scope.unpublished }
end
```

Then you can just use `PostResolver` as [GraphQL::Function](https://rmosolgo.github.io/graphql-ruby/schema/code_reuse#functions):
Then you can just use `PostResolver` as [GraphQL::Schema::Resolver](https://graphql-ruby.org/fields/resolvers.html):

```ruby
field :posts, function: PostResolver
field :posts, resolver: PostResolver
```

Options are exposed as arguments in the GraphQL query:
Expand All @@ -81,26 +81,6 @@ You can find example of most important features and plugins - [here](https://git

## Features

### Custom Types

Custom types can be define inside the search object:

```ruby
class PostResolver
include SearchObject.module(:graphql)

type do
name 'Custom Type'

field :id, !types.ID
field :title, !types.String
field :body, !types.String
end

# ...
end
```

### Documentation

Search object itself can be documented, as well as its options:
Expand Down Expand Up @@ -177,10 +157,36 @@ end

### Relay Support

Search objects can be used as [Relay Connections](https://rmosolgo.github.io/graphql-ruby/relay/connections):
Search objects can be used as [Relay Connections](https://graphql-ruby.org/relay/connections.html):

```ruby
connection :posts, Types::PostType.connection_type, function: Resolvers::PostSearch
class PostResolver
include SearchObject.module(:graphql)

type PostType.connection_type, null: false

# ...
end
```

```ruby
field :posts, resolver: PostResolver
```

### Legacy Function Support

```ruby
class PostResolver
include SearchObject.module(:graphql)

type [PostType], null: false

# ...
end
```

```ruby
field :posts, function: PostResolver
```

## Contributing
Expand Down
6 changes: 3 additions & 3 deletions example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ This is example application showing, one of the possible usages of ```SearchObje
```
gem install bundler
bundle install
rake db:create
rake db:migrate
rake db:seed
rails db:create
rails db:migrate
rails db:seed

rails server
```
Expand Down
2 changes: 1 addition & 1 deletion example/app/graphql/types/query_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
module Types
class QueryType < Types::BaseObject
field :categories, function: Resolvers::CategorySearch
field :posts, function: Resolvers::PostSearch
field :posts, resolver: Resolvers::PostSearch
end
end
2 changes: 1 addition & 1 deletion example/config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
# require "active_job/railtie"
require "active_job/railtie"
require "active_record/railtie"
require "action_controller/railtie"
# require "action_mailer/railtie"
Expand Down
2 changes: 1 addition & 1 deletion example/db/migrate/20170507175133_create_demo_tables.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def change
end

create_table :posts do |t|
t.references :category, foreign_key: true
t.references :category
t.string :title, null: false
t.index :title, unique: true
t.string :body, null: false
Expand Down
2 changes: 1 addition & 1 deletion example/db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20170507175133) do
ActiveRecord::Schema.define(version: 2017_05_07_175133) do

create_table "categories", force: :cascade do |t|
t.string "name", null: false
Expand Down
115 changes: 83 additions & 32 deletions lib/search_object/plugin/graphql.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module Plugin
module Graphql
def self.included(base)
base.include SearchObject::Plugin::Enum
base.include ::GraphQL::Schema::Member::GraphQLTypeNames
base.extend ClassMethods
end

Expand All @@ -17,34 +18,33 @@ def initialize(filters: {}, object: nil, context: {}, scope: nil)
super filters: filters, scope: scope
end

# NOTE(rstankov): GraphQL::Schema::Resolver interface
# Documentation - http://graphql-ruby.org/fields/resolvers.html#using-resolver
def resolve_with_support(args = {})
self.params = args.to_h
results
end

module ClassMethods
KEYS = %i[type default description].freeze
def option(name, options = {}, &block)
argument = Helper.build_argument(name, options)
arguments[argument.name] = argument
config[:arguments] ||= {}
config[:arguments][name.to_s] = KEYS.inject({}) do |acc, key|
acc[key] = options[key] if options.key?(key)
acc
end

options[:enum] = argument.type.values.keys if argument.type.is_a? GraphQL::EnumType
type = options.fetch(:type) { raise MissingTypeDefinitionError, name }
options[:enum] = type.values.keys if type.respond_to?(:values)

super(name, options, &block)
end

def types
GraphQL::Define::TypeDefiner.instance
end

# NOTE(rstankov): GraphQL::Function interface
# Documentation - https://rmosolgo.github.io/graphql-ruby/schema/code_reuse#functions
def call(object, args, context)
new(filters: args.to_h, object: object, context: context).results
end

def arguments
config[:args] ||= {}
end

def type(value = :default, &block)
def type(value = :default, null: true, &block)
return config[:type] if value == :default && !block_given?

config[:type] = block_given? ? GraphQL::ObjectType.define(&block) : value
config[:null] = null
end

def complexity(value = :default)
Expand All @@ -64,27 +64,78 @@ def deprecation_reason(value = :default)

config[:deprecation_reason] = value
end

# NOTE(rstankov): GraphQL::Function interface (deprecated in favour of GraphQL::Schema::Resolver)
# Documentation - http://graphql-ruby.org/guides
def call(object, args, context)
new(filters: args.to_h, object: object, context: context).results
end

# NOTE(rstankov): Used for GraphQL::Function
def types
GraphQL::Define::TypeDefiner.instance
end

# NOTE(rstankov): Used for GraphQL::Function
def arguments
(config[:arguments] || {}).inject({}) do |acc, (name, options)|
argument = GraphQL::Argument.new
argument.name = name.to_s
argument.type = options.fetch(:type) { raise MissingTypeDefinitionError, name }
argument.default_value = options[:default] if options.key? :default
argument.description = options[:description] if options.key? :description

acc[name] = argument
acc
end
end

# NOTE(rstankov): Used for GraphQL::Schema::Resolver
def field_options
{
type: type,
description: description,
extras: [],
resolver_method: :resolve_with_support,
resolver_class: self,
deprecation_reason: deprecation_reason,
arguments: (config[:arguments] || {}).inject({}) do |acc, (name, options)|
acc[name] = ::GraphQL::Schema::Argument.new(
name: name.to_s,
type: options.fetch(:type) { raise MissingTypeDefinitionError, name },
description: options[:description],
required: !!options[:required],
default_value: options.fetch(:default) { ::GraphQL::Schema::Argument::NO_DEFAULT },
owner: self
)
acc
end,
null: !!config[:null],
complexity: complexity
}
end

# NOTE(rstankov): Used for GraphQL::Schema::Resolver
def visible?(_context)
true
end

# NOTE(rstankov): Used for GraphQL::Schema::Resolver
def accessible?(_context)
true
end

# NOTE(rstankov): Used for GraphQL::Schema::Resolver
def authorized?(_object, _context)
true
end
end

class MissingTypeDefinitionError < ArgumentError
def initialize(name)
super "GraphQL type has to passed as :type to '#{name}' option"
end
end

# :api: private
module Helper
module_function

def build_argument(name, options)
argument = GraphQL::Argument.new
argument.name = name.to_s
argument.type = options.fetch(:type) { raise MissingTypeDefinitionError, name }
argument.default_value = options[:default] if options.key? :default
argument.description = options[:description] if options.key? :description
argument
end
end
end
end
end
2 changes: 1 addition & 1 deletion lib/search_object/plugin/graphql/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module SearchObject
module Plugin
module Graphql
VERSION = '0.1'
VERSION = '0.2'
end
end
end
4 changes: 2 additions & 2 deletions search_object_graphql.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ Gem::Specification.new do |spec|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ['lib']

spec.add_dependency 'graphql', '~> 1.5'
spec.add_dependency 'search_object', '~> 1.2'
spec.add_dependency 'graphql', '~> 1.8'
spec.add_dependency 'search_object', '~> 1.2.2'

spec.add_development_dependency 'coveralls'
spec.add_development_dependency 'rake'
Expand Down
Loading