Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
1 change: 1 addition & 0 deletions gemfiles/mongoid_7.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ if RUBY_VERSION >= "3.0"
gem "evt"
end
gem "fiber-storage"
gem "concurrent-ruby", "1.3.4"

gemspec path: "../"
1 change: 1 addition & 0 deletions gemfiles/rails_7.0.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ gem "sqlite3", "~> 1.4", platform: :ruby
gem "sequel"
gem "evt"
gem "async"
gem "concurrent-ruby", "1.3.4"

gemspec path: "../"
22 changes: 22 additions & 0 deletions guides/type_definitions/enums.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,25 @@ value "AUDIO", value: :audio
Then, GraphQL inputs of `AUDIO` will be converted to `:audio` and Ruby values of `:audio` will be converted to `"AUDIO"` in GraphQL responses.

Enum classes are never instantiated and their methods are never called.

You can get the GraphQL name of the enum value using the method matching its downcased name:

```ruby
Types::MediaCategory.audio # => "AUDIO"
```

You can pass a `value_method:` to override the value of the generated method:

```ruby
value "AUDIO", value: :audio, value_method: :lo_fi_audio

# ...

Types::MediaCategory.lo_fi_audio # => "AUDIO"
```

Also, you can completely skip the method generation by setting `value_method` to `false`

```ruby
value "AUDIO", value: :audio, value_method: false
```
2 changes: 1 addition & 1 deletion lib/graphql/introspection/directive_location_enum.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class DirectiveLocationEnum < GraphQL::Schema::Enum
"a __DirectiveLocation describes one such possible adjacencies."

GraphQL::Schema::Directive::LOCATIONS.each do |location|
value(location.to_s, GraphQL::Schema::Directive::LOCATION_DESCRIPTIONS[location], value: location)
value(location.to_s, GraphQL::Schema::Directive::LOCATION_DESCRIPTIONS[location], value: location, value_method: false)
end
introspection true
end
Expand Down
20 changes: 19 additions & 1 deletion lib/graphql/schema/enum.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,16 @@ class << self
# @option kwargs [String] :description, the GraphQL description for this value, present in documentation
# @option kwargs [String] :comment, the GraphQL comment for this value, present in documentation
# @option kwargs [::Object] :value the translated Ruby value for this object (defaults to `graphql_name`)
# @option kwargs [::Object] :value_method, the method name to fetch `graphql_name` (defaults to `graphql_name.downcase`)
# @option kwargs [String] :deprecation_reason if this object is deprecated, include a message here
# @return [void]
# @see {Schema::EnumValue} which handles these inputs by default
def value(*args, **kwargs, &block)
def value(*args, value_method: nil, **kwargs, &block)
kwargs[:owner] = self
value = enum_value_class.new(*args, **kwargs, &block)

generate_value_method(value, value_method)

key = value.graphql_name
prev_value = own_values[key]
case prev_value
Expand Down Expand Up @@ -223,6 +227,20 @@ def inherited(child_class)
def own_values
@own_values ||= {}
end

def generate_value_method(value, configured_value_method)
return if configured_value_method == false

value_method_name = configured_value_method || value.graphql_name.downcase

if respond_to?(value_method_name.to_sym)
$stderr << "Failed to define value method for :#{value_method_name}, because " \
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiousity ... is this different than warn(...) ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope :)

"#{value.owner.name} already responds to that method. Use `value_name:` to override the method name."
return
end

instance_eval("def #{value_method_name}; #{value.graphql_name.inspect}; end;")
end
end

enum_value_class(GraphQL::Schema::EnumValue)
Expand Down
39 changes: 39 additions & 0 deletions spec/graphql/schema/enum_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,45 @@
end
end

describe "value methods" do
it "defines default methods to fetch graphql names" do
assert_equal enum.string, "STRING"
assert_equal enum.woodwind, "WOODWIND"
assert_equal enum.brass, "BRASS"
assert_equal enum.didgeridoo, "DIDGERIDOO"
assert_equal enum.keys, "KEYS"
end

describe "when value_method is configured" do
it "use custom method" do
assert_equal enum.respond_to?(:percussion), false
assert_equal enum.precussion_custom_value_method, "PERCUSSION"
end
end

describe "when value_method conflicts with existing method" do
it "does not define method and emits warning" do
expected_message = "Failed to define value method for :value, because " \
"ConflictEnum already responds to that method. Use `value_name:` to override the method name."

assert_warns(expected_message) do
conflict_enum = Class.new(GraphQL::Schema::Enum)
Object.const_set("ConflictEnum", conflict_enum)
already_defined_method = conflict_enum.method(:value)
conflict_enum.value "VALUE", "Makes conflict"
assert_equal conflict_enum.method(:value), already_defined_method
end
end

end

describe "when value_method = false" do
it "does not define method" do
assert_equal enum.respond_to?(:silence), false
end
end
end

describe "type info" do
it "tells about the definition" do
assert_equal "Family", enum.graphql_name
Expand Down
5 changes: 3 additions & 2 deletions spec/support/jazz.rb
Original file line number Diff line number Diff line change
Expand Up @@ -240,12 +240,13 @@ class Family < BaseEnum
value "STRING", "Makes a sound by vibrating strings", value: :str, custom_setting: 1
value :WOODWIND, "Makes a sound by vibrating air in a pipe"
value :BRASS, "Makes a sound by amplifying the sound of buzzing lips"
value "PERCUSSION", "Makes a sound by hitting something that vibrates"
value "PERCUSSION", "Makes a sound by hitting something that vibrates",
value_method: :precussion_custom_value_method
value "DIDGERIDOO", "Makes a sound by amplifying the sound of buzzing lips", deprecation_reason: "Merged into BRASS"
value "KEYS" do
description "Neither here nor there, really"
end
value "SILENCE", "Makes no sound", value: false
value "SILENCE", "Makes no sound", value: false, value_method: false
end

class InstrumentType < BaseObject
Expand Down
Loading