Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

uninitialized constant Resolvers::MyResolver::MyModel - model not loaded in some cases? #66

Closed
shaneardell opened this issue Feb 27, 2019 · 4 comments

Comments

@shaneardell
Copy link

shaneardell commented Feb 27, 2019

I decided to break my resolver functions out into actual Resolver classes (in an app/graphql/resolvers directory), mainly because my app is already pretty big and having all of the field definitions and resolver functions in my QueryType class was getting unruly and the problem was only going to get worse as our product grows.

All my request specs pass just like before I broke them out, and it all seems to be working fine on our dev app on Heroku, both querying GraphQL directly and when using the GraphiQL playground.

However, I noticed that I'm getting this error when I try to play with it in GraphiQL on local, but only in some cases, and it seems not consistently:

uninitialized constant Resolvers::MyResolver::MyModel

An example:

A Recipient belongs to an Organization

My base Resolver:

class Resolvers::Base < GraphQL::Schema::Resolver
  def authorize_resource(resource, action)
    return if Pundit.policy(context[:current_user], resource).send("#{action}?")

    raise GraphQL::ExecutionError, "Unauthorized: #{action} #{resource}"
  end
end

A problematic Resolver:

class Resolvers::Questions < Resolvers::Base
  type [QuestionType], null: false

  argument :organization_uuid, ID, required: false

  def resolve(organization_uuid: nil)
    user         = context[:current_user]
    organization = Organization.friendly.find(organization_uuid) if organization_uuid

    if organization.present?
      authorize_resource(organization, 'show')
      organization.questions
    else
      Question.where(organization_uuid: user.organization_uuids)
    end
  end
end

When I run the query:

query {
  questions(organizationUuid: "14afb4cc-3ab8-11e9-b185-186590df942f") {
    uuid
  }
}

It works. However, when I run it without scoping it to a specific organization, ie:

query {
  questions {
    uuid
  }
}

I get the error uninitialized constant Resolvers::Questions::Question.

Why would the Organization model be loaded just fine but not the Question model? It seems like it's looking for the model inside the existing class in the Question case, but looking outside of it (and finding it succesfully) in the Organization case. Some Resolvers seem to work just fine with zero problems.

I tried tweaking the autoload_paths in config/application.rb but haven't had any success yet. I read that Rails handles autoloading different in production vs local and that messing with it is risky, anyway. That said, I have my types folder added. When I tried added my resolvers folder, there were all sorts of errors.

Here is my autoload paths list:

.../app/graphql/types
.../app/assets
.../app/channels
.../app/controllers
.../app/controllers/concerns
.../app/graphql
.../app/helpers
.../app/javascript
.../app/jobs
.../app/mailers
.../app/models
.../app/models/concerns
.../app/observers
.../app/policies

Any help is appreciated.

@rmosolgo
Copy link
Owner

Hmmm, tricky! Some suggestions:

  • Was there a preceeding error in your development server output? Sometimes one error can happen, then trigger a later error because the file couldn't be loaded properly.
  • You can try adding a prefix :: to clarify that you want a top-level constant. That might help rails load it properly (although it seems like it should work as written!), eg ::Question.where(...).
  • What's the full path of the file where class Question is defined? (Is it app/models/question.rb?)

@shaneardell
Copy link
Author

shaneardell commented Feb 27, 2019

The full server output:

Started POST "/graphql" for 127.0.0.1 at 2019-02-27 13:48:01 -0700
Processing by GraphqlsController#create as */*
  Parameters: {"query"=>"query {\n  questions {\n    uuid\n  }\n}\n", "variables"=>nil, "graphql"=>{"query"=>"query {\n  questions {\n    uuid\n  }\n}\n", "variables"=>nil}}
  AuthToken Load (1.4ms)  SELECT  "auth_tokens".* FROM "auth_tokens" WHERE "auth_tokens"."expired_at" IS NULL AND "auth_tokens"."token" = $1 LIMIT $2  [["token", "**********"], ["LIMIT", 1]]
  ↳ app/controllers/application_controller.rb:35
  User Load (0.5ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 4], ["LIMIT", 1]]
  ↳ app/controllers/application_controller.rb:42
   (0.1ms)  BEGIN
  ↳ app/controllers/application_controller.rb:42
  AuthToken Update (0.3ms)  UPDATE "auth_tokens" SET "last_used_at" = $1, "updated_at" = $2 WHERE "auth_tokens"."id" = $3  [["last_used_at", "2019-02-27 20:48:01.568622"], ["updated_at", "2019-02-27 20:48:01.571236"], ["id", 1]]
  ↳ app/controllers/application_controller.rb:42
   (0.3ms)  COMMIT
  ↳ app/controllers/application_controller.rb:42
Completed 500 Internal Server Error in 24ms (ActiveRecord: 6.2ms)



NameError (uninitialized constant Resolvers::Questions::Question):

app/graphql/resolvers/questions.rb:16:in `resolve'
app/controllers/graphqls_controller.rb:31:in `create'

I'm not seeing anything that points to a preceding error.

Adding the :: prefix does work, but feels weird.

The Question class is defined as a Rails model (app/models/question.rb). The core of the app can be thought of like a versioned question bank.

class Question < ApplicationRecord
  include Trackable
  include Identifiable

  belongs_to :organization
  has_many   :question_versions

  accepts_nested_attributes_for :question_versions

  validates :name, presence: true

  enum field_type: [
    :short_text,
    :paragraph_text,
    :number,
    :date,
    :time,
    :single_select,
    :multi_select,
    :likert,
    :photo,
    :audio,
    :video,
  ]

  def latest_version
    question_versions.order(:version_number).last
  end
end

@rmosolgo
Copy link
Owner

Thanks for sharing! I was hoping to see a smoking gun, but nothin' 😖

I guess since the adding :: works, we can be sure that the file does load correctly.

Sorry, I don't really have any other suggestion :(

If you don't like adding the explicit ::, the other option is to manually load the file using require_dependency at the top of the file, eg

require_dependency "question"

That will make sure Rails loads the file and can reload it as needed.

Sorry, I don't have any other ideas how graphiql-rails could be involved here!

@shaneardell
Copy link
Author

Yeah, it occurs to me I probably should have opened the issue under the graphql-ruby project instead.

My gut says it has everything to do with Rails' autoloading, especially since it's behaving differently in both prod and test environments vs local.

require_dependency 'question' works and feels a little better than the :: prefix.

I'll go with that for now. Thanks so much @rmosolgo for looking into it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants