artemis 0.1.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 +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +36 -0
- data/Appraisals +32 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +181 -0
- data/Rakefile +14 -0
- data/artemis.gemspec +28 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/gemfiles/.bundle/config +2 -0
- data/gemfiles/rails_42.gemfile +12 -0
- data/gemfiles/rails_50.gemfile +11 -0
- data/gemfiles/rails_51.gemfile +11 -0
- data/gemfiles/rails_52.gemfile +11 -0
- data/gemfiles/rails_edge.gemfile +14 -0
- data/lib/artemis.rb +3 -0
- data/lib/artemis/adapters.rb +25 -0
- data/lib/artemis/adapters/abstract_adapter.rb +46 -0
- data/lib/artemis/adapters/curb_adapter.rb +56 -0
- data/lib/artemis/adapters/net_http_adapter.rb +51 -0
- data/lib/artemis/adapters/net_http_persistent_adapter.rb +46 -0
- data/lib/artemis/adapters/test_adapter.rb +29 -0
- data/lib/artemis/client.rb +245 -0
- data/lib/artemis/exceptions.rb +19 -0
- data/lib/artemis/graphql_endpoint.rb +54 -0
- data/lib/artemis/railtie.rb +50 -0
- data/lib/artemis/version.rb +3 -0
- data/lib/generators/artemis/USAGE +11 -0
- data/lib/generators/artemis/install_generator.rb +59 -0
- data/lib/generators/artemis/templates/.gitkeep +0 -0
- data/lib/generators/artemis/templates/client.rb +10 -0
- data/lib/generators/artemis/templates/graphql.yml +19 -0
- data/lib/tasks/artemis.rake +47 -0
- data/spec/adapters_spec.rb +106 -0
- data/spec/autoloading_spec.rb +146 -0
- data/spec/callbacks_spec.rb +46 -0
- data/spec/client_spec.rb +161 -0
- data/spec/endpoint_spec.rb +45 -0
- data/spec/fixtures/metaphysics.rb +2 -0
- data/spec/fixtures/metaphysics/_artist_fragment.graphql +4 -0
- data/spec/fixtures/metaphysics/artist.graphql +7 -0
- data/spec/fixtures/metaphysics/artists.graphql +8 -0
- data/spec/fixtures/metaphysics/artwork.graphql +8 -0
- data/spec/fixtures/metaphysics/schema.json +49811 -0
- data/spec/spec_helper.rb +48 -0
- metadata +219 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
module Artemis
|
2
|
+
class Error < StandardError
|
3
|
+
end
|
4
|
+
|
5
|
+
class EndpointNotFound < Error
|
6
|
+
end
|
7
|
+
|
8
|
+
class ConfigurationError < Error
|
9
|
+
end
|
10
|
+
|
11
|
+
class GraphQLFileNotFound < Error
|
12
|
+
end
|
13
|
+
|
14
|
+
class GraphQLError < Error
|
15
|
+
end
|
16
|
+
|
17
|
+
class GraphQLServerError < GraphQLError
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/core_ext/hash/deep_merge'
|
4
|
+
require 'active_support/core_ext/hash/keys'
|
5
|
+
require 'active_support/core_ext/object/blank'
|
6
|
+
require 'active_support/core_ext/string/inflections'
|
7
|
+
require 'graphql/client'
|
8
|
+
|
9
|
+
require 'artemis/adapters'
|
10
|
+
require 'artemis/exceptions'
|
11
|
+
|
12
|
+
module Artemis
|
13
|
+
class GraphQLEndpoint
|
14
|
+
# Hash object that holds references to adapter instances.
|
15
|
+
ENDPOINT_INSTANCES = {}
|
16
|
+
|
17
|
+
private_constant :ENDPOINT_INSTANCES
|
18
|
+
|
19
|
+
class << self
|
20
|
+
##
|
21
|
+
# Provides an endpoint instance specified in the +configuration+. If the endpoint is not found in
|
22
|
+
# +ENDPOINT_INSTANCES+, it'll raise an exception.
|
23
|
+
def lookup(service_name)
|
24
|
+
ENDPOINT_INSTANCES[service_name.to_s.underscore] || raise(Artemis::EndpointNotFound, "Service `#{service_name}' not registered.")
|
25
|
+
end
|
26
|
+
|
27
|
+
def register!(service_name, configurations)
|
28
|
+
ENDPOINT_INSTANCES[service_name.to_s.underscore] = new(service_name.to_s, configurations.symbolize_keys)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :name, :url, :adapter, :timeout, :schema_path, :pool_size
|
33
|
+
|
34
|
+
def initialize(name, url: , adapter: :net_http, timeout: 30, schema_path: nil, pool_size: 5)
|
35
|
+
@name, @url, @adapter, @timeout, @schema_path, @pool_size = name.to_s, url, adapter, timeout, schema_path, pool_size
|
36
|
+
|
37
|
+
@mutex_for_schema = Mutex.new
|
38
|
+
@mutex_for_connection = Mutex.new
|
39
|
+
end
|
40
|
+
|
41
|
+
def schema
|
42
|
+
@schema || @mutex_for_schema.synchronize do
|
43
|
+
@schema ||= ::GraphQL::Client.load_schema(schema_path.presence || connection)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
alias load_schema! schema
|
47
|
+
|
48
|
+
def connection
|
49
|
+
@connection || @mutex_for_connection.synchronize do
|
50
|
+
@connection ||= ::Artemis::Adapters.lookup(adapter).new(url, service_name: name, timeout: timeout, pool_size: pool_size)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'active_support/file_update_checker'
|
2
|
+
|
3
|
+
module Artemis
|
4
|
+
class Railtie < ::Rails::Railtie #:nodoc:
|
5
|
+
initializer 'graphql.client.attach_log_subscriber' do
|
6
|
+
if !defined?(GraphQL::Client::LogSubscriber)
|
7
|
+
require "graphql/client/log_subscriber"
|
8
|
+
GraphQL::Client::LogSubscriber.attach_to :graphql
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
initializer 'graphql.client.set_query_paths' do |app|
|
13
|
+
app.paths.add "app/operations"
|
14
|
+
|
15
|
+
Artemis::Client.query_paths = app.paths["app/operations"].existent
|
16
|
+
end
|
17
|
+
|
18
|
+
initializer 'graphql.client.set_reloader', after: 'graphql.client.set_query_paths' do |app|
|
19
|
+
files_to_watch = Artemis::Client.query_paths.map {|path| [path, ["graphql"]] }.to_h
|
20
|
+
|
21
|
+
app.reloaders << ActiveSupport::FileUpdateChecker.new([], files_to_watch) do
|
22
|
+
endpoint_names = app.config_for(:graphql).keys
|
23
|
+
endpoint_names.each do |endpoint_name|
|
24
|
+
Artemis::Client.query_paths.each do |path|
|
25
|
+
FileUtils.touch("#{path}/#{endpoint_name}.rb")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
initializer 'graphql.client.load_config' do |app|
|
32
|
+
# TODO: Remove the +rescue+ call
|
33
|
+
(app.config_for(:graphql) rescue {}).each do |endpoint_name, options|
|
34
|
+
Artemis::GraphQLEndpoint.register!(endpoint_name, { 'schema_path' => app.root.join("vendor/graphql/schema/#{endpoint_name}.json").to_s }.merge(options))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
initializer 'graphql.client.preload', after: 'graphql.client.load_config' do |app|
|
39
|
+
if app.config.eager_load
|
40
|
+
app.config_for(:graphql).keys.each do |endpoint_name|
|
41
|
+
endpoint_name.to_s.camelize.constantize.preload!
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
rake_tasks do
|
47
|
+
load "tasks/artemis.rake"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
Description:
|
2
|
+
Generates a client stub and config files and downloads the schema from the given endpoint.
|
3
|
+
|
4
|
+
Example:
|
5
|
+
rails generate install Artsy https://metaphysics-production.artsy.net
|
6
|
+
|
7
|
+
This will create:
|
8
|
+
app/operations/artsy.rb
|
9
|
+
app/operations/artsy/.gitkeep
|
10
|
+
config/graphql.yml
|
11
|
+
vendor/graphql/schema/artsy.json
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'graphql/client'
|
2
|
+
require 'graphql/client/http'
|
3
|
+
|
4
|
+
class Artemis::InstallGenerator < Rails::Generators::NamedBase
|
5
|
+
source_root File.expand_path('../templates', __FILE__)
|
6
|
+
|
7
|
+
argument :endpoint_url, type: :string, banner: "The endpoint URL for a GraphQL service"
|
8
|
+
|
9
|
+
class_option :authorization, type: :string, default: nil, aliases: "-A"
|
10
|
+
|
11
|
+
def generate_client
|
12
|
+
template "client.rb", client_file_name
|
13
|
+
create_file query_dir_gitkeep, ""
|
14
|
+
end
|
15
|
+
|
16
|
+
def generate_config
|
17
|
+
in_root do
|
18
|
+
if behavior == :invoke && !File.exist?(config_file_name)
|
19
|
+
template "graphql.yml", config_file_name
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def download_schema
|
25
|
+
say " downloading GraphQL schema from #{endpoint_url}..."
|
26
|
+
|
27
|
+
if options['authorization'].present?
|
28
|
+
rake "graphql:schema:update SERVICE=#{file_name} AUTHORIZATION='#{options['authorization']}'"
|
29
|
+
else
|
30
|
+
rake "graphql:schema:update SERVICE=#{file_name}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def file_name # :doc:
|
37
|
+
@_file_name ||= super.underscore
|
38
|
+
end
|
39
|
+
|
40
|
+
def client_file_name
|
41
|
+
if mountable_engine?
|
42
|
+
"app/operations/#{namespaced_path}/#{file_name}.rb"
|
43
|
+
else
|
44
|
+
"app/operations/#{file_name}.rb"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def query_dir_gitkeep
|
49
|
+
if mountable_engine?
|
50
|
+
"app/operations/#{namespaced_path}/#{file_name}/.gitkeep"
|
51
|
+
else
|
52
|
+
"app/operations/#{file_name}/.gitkeep"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def config_file_name
|
57
|
+
"config/graphql.yml"
|
58
|
+
end
|
59
|
+
end
|
File without changes
|
@@ -0,0 +1,19 @@
|
|
1
|
+
default: &default
|
2
|
+
adapter: :net_http
|
3
|
+
timeout: 10
|
4
|
+
pool_size: 25
|
5
|
+
|
6
|
+
development:
|
7
|
+
<%= file_name %>:
|
8
|
+
<<: *default
|
9
|
+
url: <%= endpoint_url %>
|
10
|
+
|
11
|
+
test:
|
12
|
+
<%= file_name %>:
|
13
|
+
<<: *default
|
14
|
+
url: <%= endpoint_url %>
|
15
|
+
|
16
|
+
production:
|
17
|
+
<%= file_name %>:
|
18
|
+
<<: *default
|
19
|
+
url: <%= endpoint_url %>
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
require 'active_support/core_ext/string/inflections'
|
6
|
+
require 'graphql/client'
|
7
|
+
|
8
|
+
namespace :graphql do
|
9
|
+
namespace :schema do
|
10
|
+
desc "Downloads and saves the GraphQL schema (options: SERVICE=service_name AUTHORIZATION='token ...')"
|
11
|
+
task update: :environment do
|
12
|
+
service = if ENV['SERVICE']
|
13
|
+
ENV['SERVICE']
|
14
|
+
else
|
15
|
+
services = Rails.application.config_for(:graphql).keys
|
16
|
+
|
17
|
+
if services.size == 1
|
18
|
+
services.first
|
19
|
+
else
|
20
|
+
raise "Please specify a service name (available services: #{services.join(", ")}): rake graphql:schema:update SERVICE=service"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
headers = ENV['AUTHORIZATION'] ? { Authorization: ENV['AUTHORIZATION'] } : {}
|
25
|
+
service_class = service.camelize.constantize
|
26
|
+
schema_path = service_class.endpoint.schema_path
|
27
|
+
schema = service_class.connection
|
28
|
+
.execute(
|
29
|
+
document: GraphQL::Client::IntrospectionDocument,
|
30
|
+
operation_name: "IntrospectionQuery",
|
31
|
+
variables: {},
|
32
|
+
context: { headers: headers }
|
33
|
+
).to_h
|
34
|
+
|
35
|
+
if schema['errors'].nil? || schema['errors'].empty?
|
36
|
+
FileUtils.mkdir_p(File.dirname(schema_path))
|
37
|
+
File.open(schema_path, 'w') do |file|
|
38
|
+
file.write(JSON.pretty_generate(schema))
|
39
|
+
end
|
40
|
+
|
41
|
+
puts "saved schema to: #{schema_path.gsub("#{Dir.pwd}/", '')}"
|
42
|
+
else
|
43
|
+
raise "received error from server: #{schema}\n\n"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'rack'
|
3
|
+
|
4
|
+
describe 'Adapters' do
|
5
|
+
FakeServer = ->(env) {
|
6
|
+
case env['PATH_INFO']
|
7
|
+
when '/slow_server'
|
8
|
+
sleep 2.1
|
9
|
+
|
10
|
+
[200, {}, ['{}']]
|
11
|
+
when '/500'
|
12
|
+
[500, {}, ['Server error']]
|
13
|
+
else
|
14
|
+
body = {
|
15
|
+
data: {
|
16
|
+
body: JSON.parse(env['rack.input'].read),
|
17
|
+
headers: env.select {|key, val| key.start_with?('HTTP_') }
|
18
|
+
.collect {|key, val| [key.gsub(/^HTTP_/, ''), val.downcase] }
|
19
|
+
.to_h,
|
20
|
+
},
|
21
|
+
errors: [],
|
22
|
+
extensions: {}
|
23
|
+
}.to_json
|
24
|
+
|
25
|
+
[200, {}, [body]]
|
26
|
+
end
|
27
|
+
}
|
28
|
+
|
29
|
+
before :all do
|
30
|
+
Artemis::Adapters::AbstractAdapter.send(:attr_writer, :uri, :timeout)
|
31
|
+
|
32
|
+
@server_thread = Thread.new do
|
33
|
+
Rack::Handler::WEBrick.run(FakeServer, Port: 8000, Logger: WEBrick::Log.new('/dev/null'), AccessLog: [])
|
34
|
+
end
|
35
|
+
|
36
|
+
loop do
|
37
|
+
begin
|
38
|
+
TCPSocket.open('localhost', 8000)
|
39
|
+
break
|
40
|
+
rescue Errno::ECONNREFUSED
|
41
|
+
# no-op
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
after :all do
|
47
|
+
Rack::Handler::WEBrick.shutdown
|
48
|
+
@server_thread.terminate
|
49
|
+
end
|
50
|
+
|
51
|
+
shared_examples 'an adapter' do
|
52
|
+
describe '#execute' do
|
53
|
+
it 'makes an actual HTTP request' do
|
54
|
+
response = adapter.execute(
|
55
|
+
document: GraphQL::Client::IntrospectionDocument,
|
56
|
+
operation_name: 'IntrospectionQuery',
|
57
|
+
variables: { id: 'yayoi-kusama' },
|
58
|
+
context: { user_id: 1 }
|
59
|
+
)
|
60
|
+
|
61
|
+
expect(response['data']['body']['query']).to eq(GraphQL::Client::IntrospectionDocument.to_query_string)
|
62
|
+
expect(response['data']['body']['variables']).to eq('id' => 'yayoi-kusama')
|
63
|
+
expect(response['data']['body']['operationName']).to eq('IntrospectionQuery')
|
64
|
+
expect(response['errors']).to eq([])
|
65
|
+
expect(response['extensions']).to eq({})
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'raises an error when it receives a server error' do
|
69
|
+
adapter.uri = URI.parse('http://localhost:8000/500')
|
70
|
+
|
71
|
+
expect do
|
72
|
+
adapter.execute(document: GraphQL::Client::IntrospectionDocument, operation_name: 'IntrospectionQuery')
|
73
|
+
end.to raise_error(Artemis::GraphQLServerError, "Received server error status 500: Server error")
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'allows for overriding timeout' do
|
77
|
+
adapter.uri = URI.parse('http://localhost:8000/slow_server')
|
78
|
+
|
79
|
+
expect do
|
80
|
+
adapter.execute(document: GraphQL::Client::IntrospectionDocument, operation_name: 'IntrospectionQuery')
|
81
|
+
end.to raise_error(timeout_error)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe Artemis::Adapters::NetHttpAdapter do
|
87
|
+
let(:adapter) { Artemis::Adapters::NetHttpAdapter.new('http://localhost:8000', service_name: nil, timeout: 2, pool_size: 5) }
|
88
|
+
let(:timeout_error) { Net::ReadTimeout }
|
89
|
+
|
90
|
+
it_behaves_like 'an adapter'
|
91
|
+
end
|
92
|
+
|
93
|
+
describe Artemis::Adapters::NetHttpPersistentAdapter do
|
94
|
+
let(:adapter) { Artemis::Adapters::NetHttpPersistentAdapter.new('http://localhost:8000', service_name: nil, timeout: 2, pool_size: 5) }
|
95
|
+
let(:timeout_error) { Net::HTTP::Persistent::Error }
|
96
|
+
|
97
|
+
it_behaves_like 'an adapter'
|
98
|
+
end
|
99
|
+
|
100
|
+
describe Artemis::Adapters::CurbAdapter do
|
101
|
+
let(:adapter) { Artemis::Adapters::CurbAdapter.new('http://localhost:8000', service_name: nil, timeout: 2, pool_size: 5) }
|
102
|
+
let(:timeout_error) { Curl::Err::TimeoutError }
|
103
|
+
|
104
|
+
it_behaves_like 'an adapter'
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
describe "#{GraphQL::Client} Autoloading" do
|
2
|
+
describe ".load_constant" do
|
3
|
+
it "loads the specified constant if there is a matching graphql file" do
|
4
|
+
Metaphysics.send(:remove_const, :Artist) if Metaphysics.constants.include?(:Artist)
|
5
|
+
|
6
|
+
Metaphysics.load_constant(:Artist)
|
7
|
+
|
8
|
+
expect(defined?(Metaphysics::Artist)).to eq('constant')
|
9
|
+
end
|
10
|
+
|
11
|
+
it "does nothing and returns nil if there is no matching file" do
|
12
|
+
expect(Metaphysics.load_constant(:DoesNotExist)).to be_nil
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe ".preload!" do
|
17
|
+
it "preloads all the graphQL files in the query paths" do
|
18
|
+
%i(Artist Artwork ArtistFragment)
|
19
|
+
.select {|const_name| Metaphysics.constants.include?(const_name) }
|
20
|
+
.each {|const_name| Metaphysics.send(:remove_const, const_name) }
|
21
|
+
|
22
|
+
Metaphysics.preload!
|
23
|
+
|
24
|
+
expect(defined?(Metaphysics::Artist)).to eq('constant')
|
25
|
+
expect(defined?(Metaphysics::Artwork)).to eq('constant')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it "dynamically loads the matching GraphQL query and sets it to a constant" do
|
30
|
+
Metaphysics.send(:remove_const, :Artist) if Metaphysics.constants.include?(:Artist)
|
31
|
+
|
32
|
+
query = Metaphysics::Artist
|
33
|
+
|
34
|
+
expect(query.document.to_query_string).to eq(<<~GRAPHQL.strip)
|
35
|
+
query Metaphysics__Artist($id: String!) {
|
36
|
+
artist(id: $id) {
|
37
|
+
name
|
38
|
+
bio
|
39
|
+
birthday
|
40
|
+
}
|
41
|
+
}
|
42
|
+
GRAPHQL
|
43
|
+
end
|
44
|
+
|
45
|
+
it "dynamically loads the matching GraphQL fragment and sets it to a constant" do
|
46
|
+
Metaphysics.send(:remove_const, :ArtistFragment) if Metaphysics.constants.include?(:ArtistFragment)
|
47
|
+
|
48
|
+
query = Metaphysics::ArtistFragment
|
49
|
+
|
50
|
+
expect(query.document.to_query_string).to eq(<<~GRAPHQL.strip)
|
51
|
+
fragment Metaphysics__ArtistFragment on Artist {
|
52
|
+
hometown
|
53
|
+
deathday
|
54
|
+
}
|
55
|
+
GRAPHQL
|
56
|
+
end
|
57
|
+
|
58
|
+
it "correctly loads the matching GraphQL query even when the top-level constant with the same name exists" do
|
59
|
+
# In Ruby <= 2.4 top-level constants can be looked up through a namespace, which turned out to be a bad practice.
|
60
|
+
# This has been removed in 2.5, but in earlier versions still suffer from this behaviour.
|
61
|
+
Metaphysics.send(:remove_const, :Artist) if Metaphysics.constants.include?(:Artist)
|
62
|
+
Object.send(:remove_const, :Artist) if Object.constants.include?(:Artist)
|
63
|
+
|
64
|
+
begin
|
65
|
+
Object.send(:const_set, :Artist, 1)
|
66
|
+
|
67
|
+
Metaphysics.artist
|
68
|
+
ensure
|
69
|
+
Object.send(:remove_const, :Artist)
|
70
|
+
end
|
71
|
+
|
72
|
+
query = Metaphysics::Artist
|
73
|
+
|
74
|
+
expect(query.document.to_query_string).to eq(<<~GRAPHQL.strip)
|
75
|
+
query Metaphysics__Artist($id: String!) {
|
76
|
+
artist(id: $id) {
|
77
|
+
name
|
78
|
+
bio
|
79
|
+
birthday
|
80
|
+
}
|
81
|
+
}
|
82
|
+
GRAPHQL
|
83
|
+
end
|
84
|
+
|
85
|
+
it "raises an exception when the path was resolved but the file does not exist" do
|
86
|
+
begin
|
87
|
+
Metaphysics.graphql_file_paths << "metaphysics/removed.graphql"
|
88
|
+
|
89
|
+
expect { Metaphysics::Removed }.to raise_error(Errno::ENOENT)
|
90
|
+
ensure
|
91
|
+
Metaphysics.graphql_file_paths.delete("metaphysics/removed.graphql")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it "raises an NameError when there is no graphql file that matches the const name" do
|
96
|
+
expect { Metaphysics::DoesNotExist }.to raise_error(NameError)
|
97
|
+
end
|
98
|
+
|
99
|
+
xit "defines the query method when the matching class method gets called for the first time" do
|
100
|
+
Metaphysics.undef_method(:artwork) if Metaphysics.public_instance_methods.include?(:artwork)
|
101
|
+
|
102
|
+
Metaphysics.artwork
|
103
|
+
|
104
|
+
expect(Metaphysics.public_instance_methods).to include(:artwork)
|
105
|
+
end
|
106
|
+
|
107
|
+
it "raises an NameError when there is no graphql file that matches the class method name" do
|
108
|
+
expect { Metaphysics.does_not_exist }.to raise_error(NameError)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "raises an NameError when the class method name matches a fragment name" do
|
112
|
+
expect { Metaphysics.artist_fragment }.to raise_error(NameError)
|
113
|
+
end
|
114
|
+
|
115
|
+
it "responds to a class method that has a matching graphQL file" do
|
116
|
+
expect(Metaphysics).to respond_to(:artwork)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "does not respond to class methods that do not have a matching graphQL file" do
|
120
|
+
expect(Metaphysics).not_to respond_to(:does_not_exist)
|
121
|
+
end
|
122
|
+
|
123
|
+
xit "defines the query method when the matching instance method gets called for the first time" do
|
124
|
+
Metaphysics.undef_method(:artwork) if Metaphysics.public_instance_methods.include?(:artwork)
|
125
|
+
|
126
|
+
Metaphysics.new.artwork
|
127
|
+
|
128
|
+
expect(Metaphysics.public_instance_methods).to include(:artwork)
|
129
|
+
end
|
130
|
+
|
131
|
+
it "raises an NameError when there is no graphql file that matches the instance method name" do
|
132
|
+
expect { Metaphysics.new.does_not_exist }.to raise_error(NameError)
|
133
|
+
end
|
134
|
+
|
135
|
+
it "raises an NameError when the instance method name matches a fragment name" do
|
136
|
+
expect { Metaphysics.new.artist_fragment }.to raise_error(NameError)
|
137
|
+
end
|
138
|
+
|
139
|
+
it "responds to the method that has a matching graphQL file" do
|
140
|
+
expect(Metaphysics.new).to respond_to(:artwork)
|
141
|
+
end
|
142
|
+
|
143
|
+
it "does not respond to methods that do not have a matching graphQL file" do
|
144
|
+
expect(Metaphysics.new).not_to respond_to(:does_not_exist)
|
145
|
+
end
|
146
|
+
end
|