# undefined method error for my schema

**URL:** <https://discourse.dry-rb.org/t/undefined-method-error-for-my-schema/1593>\
**Category:** Uncategorized\
**Created:** [January 8, 2023, 2:22pm UTC](https://discourse.dry-rb.org/t/undefined-method-error-for-my-schema/1593 "2023-01-08T14:22:02Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![jedrekdomanski](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/jedrekdomanski/32/838_2.png) [@jedrekdomanski](https://discourse.dry-rb.org/u/jedrekdomanski)\
**Post date:** [January 8, 2023, 2:22pm UTC](https://discourse.dry-rb.org/t/undefined-method-error-for-my-schema/1593/1 "2023-01-08T14:22:02Z")

</div>

Hello,  
I am reaching out because I feel confused by the documentation of dry-schema. I do as the docs say but I get errors and I’m stuck.

My case is to have `apikey` key in all schemas and some required and optional keys in each individual schema.  
According to your documentation it is possible to define a base schema and make individual schemas inherit from it so that the global configuration and rules are applied to each individual schema.

Here what’s your documentation says ([dry-rb - dry-schema v1.10 - Working with schemas](https://dry-rb.org/gems/dry-schema/1.10/basics/working-with-schemas/#defining-base-schema-class))

```auto
A schema is an object which contains a list of rules that will be applied to its input when you call a schema. It returns a result object which provides an API to retrieve error messages and access to the validation output.

```

So I define my schema like so:

```ruby
class AppSchema < Dry::Schema::Params
  define do
    config.messages.load_paths << '/my/app/config/locales/en.yml'
    config.messages.backend = :i18n

    required(:apikey).filled(:string)
  end
end

# now you can build other schemas on top of the base one:
class MySchema < AppSchema
  define do
    required(:id).filled(:string)
  end
end

```

Now I would like to verify if the input was correct and if all rules passed. According to your documentation it is possible to do ([dry-rb - dry-schema v1.10 - Working with schemas](https://dry-rb.org/gems/dry-schema/1.10/basics/working-with-schemas/#working-with-error-messages))  
However when I call `errors` or `success?` I get an error.

```ruby
my_schema = MySchema.new
my_schema.errors

undefined method `errors' for #<MySchema:0x000000013c1b5ae8> (NoMethodError)

my_schema.success?
undefined method `success?' for #<MySchema:0x000000013e998a38> (NoMethodError)

```

Calling `.call` on Schema raises an error

```ruby
MySchema.call
undefined method `call' for MySchema:Class (NoMethodError)

```

I am confused. What am I missing?

---

<div class="post-metadata">

**Author:** ![jedrekdomanski](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/jedrekdomanski/32/838_2.png) [@jedrekdomanski](https://discourse.dry-rb.org/u/jedrekdomanski)\
**Post date:** [January 8, 2023, 2:50pm UTC](https://discourse.dry-rb.org/t/undefined-method-error-for-my-schema/1593/2 "2023-01-08T14:50:05Z")

</div>

I think I’ve figured this out. I need to call `#call(input)` to validate the schema with given input.

```ruby
my_schema = MySchema.new(name: 'adasd')
result = my_schema.call({id: 'adasd', name: '123', extra_key: 'asdas'})
result.success? #=> true
result.errors(full: true).to_h

```

It feels strange for someone used to `ActiveRecord` way of validating input.

```ruby
obj = MyObj.new(input)
obj.valid?

```

Is it possible to achieve this in `dry-schema` or `dry-struct`?

---

<div class="post-metadata">

**Author:** ![flash-gordon](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/flash-gordon/32/494_2.png) [@flash-gordon](https://discourse.dry-rb.org/u/flash-gordon)\
**Post date:** [January 10, 2023, 9:24am UTC](https://discourse.dry-rb.org/t/undefined-method-error-for-my-schema/1593/3 "2023-01-10T09:24:31Z")

</div>

You can create a schema and assign it to a constant: `Schema = MySchema.new`. Then use it like that:

```auto
validation_result = Schema.(input)

if validation_result.success?
  # ...
else
  # ...
end

```

There’s a shortcut for defining a schema:

```auto
MySchema = Dry::Schema.Params(parent: AppSchema) do
  required(:apikey).filled(:string)
  # so there's no need to call `MySchema.new` later on
end

```

---

<div class="post-metadata">

**Author:** ![jedrekdomanski](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/jedrekdomanski/32/838_2.png) [@jedrekdomanski](https://discourse.dry-rb.org/u/jedrekdomanski)\
**Post date:** [January 10, 2023, 10:23am UTC](https://discourse.dry-rb.org/t/undefined-method-error-for-my-schema/1593/4 "2023-01-10T10:23:54Z")

</div>

Cool, thanks! Very helpful 👍

---

<div class="post-metadata">

**Author:** ![jedrekdomanski](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/jedrekdomanski/32/838_2.png) [@jedrekdomanski](https://discourse.dry-rb.org/u/jedrekdomanski)\
**Post date:** [January 10, 2023, 6:39pm UTC](https://discourse.dry-rb.org/t/undefined-method-error-for-my-schema/1593/5 "2023-01-10T18:39:49Z")

</div>

I’ve just tried it and it seems to work but there are problems with this approach. First problem is that `required` field is not shown after calling `to_h` even if it is provided.

```ruby
AppSchema = Dry::Schema.Params do
   config.validate_keys = true
   required(:id).filled(:string)
end

EventDetails = Dry::Schema.Params(parent: AppSchema) do
   optional(:locale).filled(:string)
   optional(:domain).filled(:string)
end

input = { apikey: '1234', locale: 'EN', domain: 'example.com'}
puts EventDetails.call(input).to_h
# => {:locale=>"EN", :domain=>"example.com"}

```

Why `apikey` is not there? What am I missing?

Second problem, you can’t define namespaced schemas this way. For instance, I can’t have

```auto
# lib/my_module/another_module/my_schema.rb
MyModule::AnotherModule::MySchema = Dry::Schema.Params do
  ...
end

```

Is it possible to define schemas using `class` - the Ruby way?

---

<div class="post-metadata">

**Author:** ![jedrekdomanski](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/jedrekdomanski/32/838_2.png) [@jedrekdomanski](https://discourse.dry-rb.org/u/jedrekdomanski)\
**Post date:** [January 10, 2023, 7:19pm UTC](https://discourse.dry-rb.org/t/undefined-method-error-for-my-schema/1593/6 "2023-01-10T19:19:53Z")

</div>

I was somehow successsful but I don’t know how 🤔

```ruby
module MyModule
  module AnotherModule
    class AppSchema < Dry::Schema::Params
      define do
        config.validate_keys = true
        required(:apikey).filled(:string)
      end
    end
  end
end

module MyModule
  module AnotherModule
    class EventDetails < MyModule::AnotherModule::AppSchema
      define do
        optional(:locale).filled(:string)
        optional(:domain).filled(:string)
      end
    end
  end
end

```

event\_details = MyModule::AnotherModule::EventDetails.new  
event\_details.call(apikey: ‘1234’, locale: ‘EN’, domain: ‘test’)  
It works! 🙂

---

<div class="post-metadata">

**Author:** ![flash-gordon](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/flash-gordon/32/494_2.png) [@flash-gordon](https://discourse.dry-rb.org/u/flash-gordon)\
**Post date:** [January 11, 2023, 6:12pm UTC](https://discourse.dry-rb.org/t/undefined-method-error-for-my-schema/1593/7 "2023-01-11T18:12:49Z")

</div>

> Why `apikey` is not there? What am I missing?  
> First of, it’s `:id` in the parent schema, you didn’t define `:apikey` hence it isn’t present in the output.  
> Second, you don’t validation status but you definitely need to do this:

```ruby
input = { apikey: '1234', locale: 'EN', domain: 'example.com'}
result = EventDetails.call(input)

if result.success?
  # then call .to_h
else
  # call result.errors
end

```

To define a nested constant you should

```ruby
module MyModule
  module AnotherModule
    MySchema = Dry::Schema.Params do
       ...
    end
  end
end

```

```ruby
event_details = MyModule::AnotherModule::EventDetails.new

```

I wouldn’t create a schema on every validation. It’s not free. Your app will be faster if schemas are created once.

---

<div class="post-metadata">

**Author:** ![jedrekdomanski](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/jedrekdomanski/32/838_2.png) [@jedrekdomanski](https://discourse.dry-rb.org/u/jedrekdomanski)\
**Post date:** [January 11, 2023, 8:36pm UTC](https://discourse.dry-rb.org/t/undefined-method-error-for-my-schema/1593/8 "2023-01-11T20:36:14Z")

</div>

> First of, it’s `:id` in the parent schema, you didn’t define `:apikey` hence it isn’t present in the output.

Oh, yes, my bad, I overlooked it.

You mean it works faster if you define the schema using a block and call `.call` on the constant?

```ruby
module MyModule
  module AnotherModule
    MySchema = Dry::Schema.Params do
       ...
    end
  end
end

MyModule:: AnotherModule::MySchema.call(input)

```

rather than using `class` keyword?

```ruby
module MyModule
  module AnotherModule
    class AppSchema < Dry::Schema::Params
      define do
        config.validate_keys = true
        required(:apikey).filled(:string)
      end
    end
  end
end

schema = MyModule:: AnotherModule:: MySchema.new
result = schema.call(input)

```

---

<div class="post-metadata">

**Author:** ![flash-gordon](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/flash-gordon/32/494_2.png) [@flash-gordon](https://discourse.dry-rb.org/u/flash-gordon)\
**Post date:** [January 12, 2023, 7:29am UTC](https://discourse.dry-rb.org/t/undefined-method-error-for-my-schema/1593/9 "2023-01-12T07:29:07Z")

</div>

Yeah. I mean I didn’t measure, it’s just there’s no need to create an instance of a schema every time. It’s because we don’t mutate objects in general so the only method that is called on a schema is `call`. And it returns a new object with validation results rather than mutating anything.

In my applications, I use DI for defining schemas and validation contracts, they are created, registered in a container, and linked together during the initialization phase. They are also frozen so that I don’t get accidental sharing between requests.

---

<div class="post-metadata">

**Author:** ![jedrekdomanski](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/jedrekdomanski/32/838_2.png) [@jedrekdomanski](https://discourse.dry-rb.org/u/jedrekdomanski)\
**Post date:** [January 12, 2023, 9:59am UTC](https://discourse.dry-rb.org/t/undefined-method-error-for-my-schema/1593/10 "2023-01-12T09:59:42Z")

</div>

DI? What does it mean? 🤔

---

<div class="post-metadata">

**Author:** ![flash-gordon](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/flash-gordon/32/494_2.png) [@flash-gordon](https://discourse.dry-rb.org/u/flash-gordon)\
**Post date:** [January 12, 2023, 10:16am UTC](https://discourse.dry-rb.org/t/undefined-method-error-for-my-schema/1593/11 "2023-01-12T10:16:12Z")

</div>

dependency injection, look at dry-auto\_inject and dry-system. I also have a sample repo with effects on top of it [GitHub - flash-gordon/rt-tracker](https://github.com/flash-gordon/rt-tracker)

---

<div class="post-metadata">

**Author:** ![jedrekdomanski](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/jedrekdomanski/32/838_2.png) [@jedrekdomanski](https://discourse.dry-rb.org/u/jedrekdomanski)\
**Post date:** [January 12, 2023, 10:29am UTC](https://discourse.dry-rb.org/t/undefined-method-error-for-my-schema/1593/12 "2023-01-12T10:29:55Z")

</div>

Oh, I see. I always use DI in Ruby and I do it like this:

```ruby
MySchema = Dry::Schema.Params do
  ...
end

class MyKlass
  def initialize(schema: MySchema)
    @schema = schema
  end

  def call(input)
    result = @schema.call(input)
    ...
  end
end

```
